diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index 19dbbcb025fb9..7a9878b5bcd13 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -5,7 +5,7 @@ set -euo pipefail export KBN_NP_PLUGINS_BUILT=true echo "--- Build Kibana Distribution" -node scripts/build --debug --no-oss +node scripts/build --debug echo "--- Archive Kibana Distribution" linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" diff --git a/.buildkite/scripts/common/env.sh b/.buildkite/scripts/common/env.sh index ff09126f60b08..901fa9f50a61f 100755 --- a/.buildkite/scripts/common/env.sh +++ b/.buildkite/scripts/common/env.sh @@ -67,6 +67,6 @@ export TEST_KIBANA_HOST=localhost export TEST_KIBANA_PORT=6101 export TEST_KIBANA_URL="http://elastic:changeme@localhost:6101" export TEST_ES_URL="http://elastic:changeme@localhost:6102" -export TEST_ES_TRANSPORT_PORT=6103 +export TEST_ES_TRANSPORT_PORT=6301-6309 export TEST_CORS_SERVER_PORT=6106 export ALERTING_PROXY_PORT=6105 diff --git a/.buildkite/scripts/post_build_kibana.sh b/.buildkite/scripts/post_build_kibana.sh index ad22a224f7c55..06864b204bc3c 100755 --- a/.buildkite/scripts/post_build_kibana.sh +++ b/.buildkite/scripts/post_build_kibana.sh @@ -6,7 +6,7 @@ if [[ ! "${DISABLE_CI_STATS_SHIPPING:-}" ]]; then echo "--- Ship Kibana Distribution Metrics to CI Stats" node scripts/ship_ci_stats \ --metrics target/optimizer_bundle_metrics.json \ - --metrics node_modules/@kbn/ui-shared-deps/shared_built_assets/metrics.json + --metrics build/kibana/node_modules/@kbn/ui-shared-deps/shared_built_assets/metrics.json fi echo "--- Upload Build Artifacts" diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 9445d02265725..e6fdda9f1cb23 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -28,6 +28,11 @@ jobs: - name: Install Actions run: npm install --production --prefix ./actions + - name: Fix Version Label Gaps + uses: ./actions/fix-version-gaps + with: + github_token: ${{secrets.KIBANAMACHINE_TOKEN}} + - name: Run Backport uses: ./actions/backport with: diff --git a/.github/workflows/fix-version-gaps.yml b/.github/workflows/fix-version-gaps.yml new file mode 100644 index 0000000000000..ea832ff22a68a --- /dev/null +++ b/.github/workflows/fix-version-gaps.yml @@ -0,0 +1,35 @@ +on: + pull_request: + branches: + - master + types: + - closed + +jobs: + gaps: + name: Fix Version Label Gaps + # This fix also runs as part of the backport action (because backport depends on the labels) + # So we only need to trigger it for merged PRs that also won't be auto-backported + if: | + github.event.pull_request.merged == true + && !contains(github.event.pull_request.labels.*.name, 'auto-backport') + && !( + (github.event.action == 'labeled' && github.event.label.name == 'auto-backport') + || (github.event.action == 'closed') + ) + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: 'elastic/kibana-github-actions' + ref: main + path: ./actions + + - name: Install Actions + run: npm install --production --prefix ./actions + + - name: Run Fix Gaps + uses: ./actions/fix-version-gaps + with: + github_token: ${{secrets.KIBANAMACHINE_TOKEN}} diff --git a/.i18nrc.json b/.i18nrc.json index 36b3777fd1f5a..2709d5ad7a671 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -19,6 +19,7 @@ "expressions": "src/plugins/expressions", "expressionError": "src/plugins/expression_error", "expressionImage": "src/plugins/expression_image", + "expressionMetric": "src/plugins/expression_metric", "expressionRepeatImage": "src/plugins/expression_repeat_image", "expressionRevealImage": "src/plugins/expression_reveal_image", "expressionShape": "src/plugins/expression_shape", diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index d0c19f8628361..9be0170b67b74 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -1,5 +1,5 @@ [[release-notes]] -= Release Notes += Release notes [partintro] -- @@ -19,3 +19,242 @@ This section summarizes the changes in each release. == {kib} 8.0.0-alpha1 coming[8.0.0] + +The following changes are released for the first time in {kib} 8.0.0-alpha1. Review the changes, then use the <> to complete the upgrade. + +[float] +[[breaking-changes-8.0.0]] +=== Breaking changes + +Breaking changes can prevent your application from optimal operation and performance. Review the breaking changes, then mitigrate the impact to your appilication. + +// tag::notable-breaking-changes[] + +[float] +[[enterprise-search-change]] +==== Enterprise Search changes + +[discrete] +[[breaking-106307]] +.Required security plugin in 8.0 +[%collapsible] +==== +*Details* + +Enterprise Search now requires that you enable X-Pack Security. For more information, refer to {kibana-pull}106307[#106307] + +*Impact* + +Enable X-Pack Security. +==== + +[float] +[[index-pattern-change]] +==== Index pattern changes + +[discrete] +[[breaking-35173]] +.Removed support for time-based interval index patterns +[%collapsible] +==== +*Details* + +Time-based interval index patterns were deprecated in 5.x. In 6.x, you could no longer create time-based interval index patterns, but they continued to function as expected. Support for these index patterns has been removed in 8.0. For more information, refer to {kibana-pull}35173[#35173] + +*Impact* + +You must migrate your time_based index patterns to a wildcard pattern. For example, logstash-*. +==== + +[float] +[[operations-changes]] +==== Operations changes + +[discrete] +[[breaking-93835]] +.Removed platform from archive root directory +[%collapsible] +==== +*Details* + +For the `.tar.gz` and `.zip` archives, `platform` has been removed from the `root` folder name. For more information, refer to {kibana-pull}93835[#93835] + +*Impact* + +The `root` folder name now appears as `kibana-8.0.0-SNAPSHOT-linux-aarch64.tar.gz -> kibana-8.0.0-SNAPSHOT`. +==== + +[discrete] +[[breaking-90511]] +.Removed default support for TLS v1.0 and v1.1 +[%collapsible] +==== +*Details* + +The default support for TLS v1.0 and v1.1 has been removed. For more information, refer to {kibana-pull}90511[#90511] + +*Impact* + +To enable support, set the environment variable to `NODE_OPTIONS=--tls-min-1.0`. +==== + +[discrete] +[[breaking-74424]] +.Removed support for sysv init +[%collapsible] +==== +*Details* + +Systems that don't have `service` aliased to use kibana.service are unable to use `service start kibana`. For more information, refer to {kibana-pull}74424[#74424] + +*Impact* + +If your system doesn't have `service` aliased to use kibana.service, use `systemctl start kibana.service`. +==== + +[discrete] +[[breaking-42353]] +.Disabled response logging as a default +[%collapsible] +==== +*Details* + +By default, responses are not logged. Previously, responses were logged if `logging.json` was set to `true`, `logging.dest` was specified, or a TTY was detected. For more information, refer to {kibana-pull}42353[#42353] + +*Impact* + +To log responses, set `logging.events.response=*` in kibana.yml. +==== + +[float] +[[reporting-changes-8.0.0-alpha1]] +==== Reporting changes + +[discrete] +[[breaking-52539]] +.Removed legacy Reporting job params compatibility shim +[%collapsible] +==== +*Details* + +*Reporting* is no longer compatible with POST URL snippets generated with {kib} 6.2.0 and earlier. For more information, refer to {kibana-pull}52539[#52539] + +*Impact* + +If you use POST URL snippets to automatically generate PDF reports, regenerate the POST URL strings. +==== + +[float] +[[rest-api-changes]] +==== Security changes + +[discrete] +[[breaking-47929]] +.Removed `/api/security/v1/saml` route +[%collapsible] +==== +*Details* + +The `/api/security/v1/saml` route has been removed and is reflected in the kibana.yml `server.xsrf.whitelist` setting, {es}, and the Identity Provider SAML settings. For more information, refer to {kibana-pull}47929[#47929] + +*Impact* + +Use the `/api/security/saml/callback` route, or wait to upgrade to 8.0.0-alpha2 when the `/api/security/saml/callback` route breaking change is reverted. +==== + +[discrete] +[[breaking-41700]] +.Reject legacy browsers by default +[%collapsible] +==== +*Details* + +To provide the maximum level of protection for most installations, the csp.strict config is now enabled by default. Legacy browsers not supported by Kibana, such as IE11, are unable to access {kib} unless explicitly enabled. All browsers officially supported by Kibana do not have this issue. For more information, refer to {kibana-pull}41700[#41700] + +*Impact* + +To enable support for legacy browsers, set `csp.strict: false` in kibana.yml. +==== + +[float] +[[settings-changes-8.0.0-alpha1]] +==== Settings changes + +[discrete] +[[breaking-106061]] +.Use new session timeout defaults +[%collapsible] +==== +*Details* + +The default values for the session timeout `xpack.security.session.{lifespan|idleTimeout}` settings have changed. For more information, refer to {kibana-pull}106061[#106061] + +*Impact* + +Use the following default values: + +* `xpack.security.session.idleTimeout: 1h` +* `xpack.security.session.lifespan: 30d` +==== + +[discrete] +[[breaking-87114]] +.Removed support for setting `server.host` to '0' +[%collapsible] +==== +*Details* + +Support for configuring {kib} with `0` as the `server.host` has been removed. Please use `0.0.0.0` instead. For more information, refer to {kibana-pull}87114[#87114] + +*Impact* + +You are now unable to use `0` as the `server.host`. +==== + +[discrete] +[[breaking-38657]] +.Removed `xpack.security.authProviders` and `xpack.security.public` +[%collapsible] +==== +*Details* + +The `xpack.security.public` and `xpack.security.authProviders` settings have been removed. For more information, refer to {kibana-pull}38657[#38657] + +*Impact* + +Use the `xpack.security.authc.saml.realm` setting. +==== + +[discrete] +[[breaking-22696]] +.Removed useUTC deprecation +[%collapsible] +==== +*Details* + +The `logging.useUTC` setting has been removed. For more information, refer to {kibana-pull}22696[#22696] + +*Impact* + +The default timezone is UTC. To change the timezone, set `logging.timezone: false` in kibana.yml. Change the timezone when the system, such as a docker container, is configured for a nonlocal timezone. +==== + +// end::notable-breaking-changes[] + +[float] +[[deprecations-8.0.0]] +=== Deprecations + +The following functionality is deprecated in 8.0.0, and will be removed in 9.0.0. Deprecated functionality does not have an immediate impact on your application, but we strongly recommend you make the necessary updates after you complete the upgrade. + +[discrete] +[[deprecation-74424]] +.Removed support for SysV init +[%collapsible] +==== +*Details* + +Systems that don't have `service` aliased to use kibana.service are unable to use `service start kibana`. For more information, refer to {kibana-pull}74424[#74424] + +*Impact* + +If your system doesn't have `service` aliased to use kibana.service, use `systemctl start kibana.service`. +==== + +[discrete] +[[deprecation-33603]] +.Removed `xpack:defaultAdminEmail` setting +[%collapsible] +==== +*Details* + +The `xpack:default_admin_email` setting for monitoring use has been removed. For more information, refer to {kibana-pull}33603[#33603] + +*Impact* + +Use the `xpack.monitoring.clusterAlertsEmail` in kibana.yml. +==== + +[float] +[[enhancements-and-bug-fixes-v8.0.0]] +=== Bug fix + +The 8.0.0-alpha1 release includes the following bug fix. + +Operations:: +* Moves systemd service to /usr/lib/systemd/system {kibana-pull}83571[#83571] + +//[[release-notes-8.0.0]] +//== {kib} 8.0.0 + +//coming::[8.0.0] diff --git a/docs/canvas/canvas-edit-workpads.asciidoc b/docs/canvas/canvas-edit-workpads.asciidoc index 9f2808c9ad451..0dd7351e9cbcc 100644 --- a/docs/canvas/canvas-edit-workpads.asciidoc +++ b/docs/canvas/canvas-edit-workpads.asciidoc @@ -12,6 +12,7 @@ When you frequently use copy and paste, create variables to easily reuse strings each element instead of updating them manually. . Create the variables. +.. Expand the *Variables* options. .. Click *Add a variable*. .. Specify the variable options, then click *Save changes*. @@ -25,12 +26,12 @@ For example, to change the index pattern for a set of charts: . Specify the variable options. + [role="screenshot"] -image::images/specify_variable_syntax.png[Image describing how to specify the variable syntax] +image::images/specify_variable_syntax.png[Variable syntax options] + . Copy the variable, then apply it to each element you want to update in the *Expression editor*. + [role="screenshot"] -image::images/copy_variable_syntax.png[Image demonstrating expression editor] +image::images/copy_variable_syntax.png[Copied variable syntax pasted in the Expression editor] [float] [[apply-changes-to-the-entire-workpad]] @@ -84,9 +85,6 @@ To use an element with the same functionality and appearance in multiple places, Select the element, then click *Edit > Clone*. -[role="screenshot"] -image::images/clone_element.gif[Image showing how to clone elements] - [float] [[move-and-resize-elements]] ==== Move and resize elements diff --git a/docs/canvas/canvas-present-workpad.asciidoc b/docs/canvas/canvas-present-workpad.asciidoc index 438e09b701fa3..0dab86fd0f444 100644 --- a/docs/canvas/canvas-present-workpad.asciidoc +++ b/docs/canvas/canvas-present-workpad.asciidoc @@ -9,16 +9,10 @@ When you are ready to present your workpad, use and enable the presentation opti .. From the workpad menu, click *View > Autoplay settings*. .. Under *Change cycling interval*, select the interval you want to use, or *Set a custom interval*. -+ -[role="screenshot"] -image::images/canvas-autoplay-interval.png[Element autoplay interval] . To enable autoplay, click *View > Turn autoplay on*. . To start your presentation, click *View > Enter fullscreen mode*. -+ -[role="screenshot"] -image::images/canvas-fullscreen.png[Image showing how to enter fullscreen mode from view dropdown] . When you are ready to exit fullscreen mode, press Esc. @@ -31,9 +25,6 @@ To get a closer look at a portion of your workpad, use the zoom options. . Click *View > Zoom*. . Select the zoom option. -+ -[role="screenshot"] -image::images/canvas-zoom-controls.png[Zoom controls, also in view dropdown] [float] [[configure-auto-refresh-interval]] @@ -45,7 +36,4 @@ Change how often the data refreshes on your workpad. . Select the interval you want to use, or *Set a custom interval*. + -[role="screenshot"] -image::images/canvas-refresh-interval.png[Element data refresh interval] -+ To manually refresh the data, click image:canvas/images/canvas-refresh-data.png[Canvas refresh data button]. diff --git a/docs/canvas/canvas-tutorial.asciidoc b/docs/canvas/canvas-tutorial.asciidoc index 89114affb9322..8700161df2bf6 100644 --- a/docs/canvas/canvas-tutorial.asciidoc +++ b/docs/canvas/canvas-tutorial.asciidoc @@ -9,9 +9,9 @@ To familiarize yourself with *Canvas*, add the Sample eCommerce orders data, the To create a workpad of the eCommerce store data, add the data set, then create the workpad. -. On the {kib} *Home* page, click *Try our sample data*. +. Go to the {kib} *Home* page, then click *Try our sample data*. -. From *Sample eCommerce orders data*, click *Add data*. +. On the *Sample eCommerce orders data* card, click *Add data*. . Open the main menu, then click *Canvas*. @@ -26,8 +26,8 @@ To customize your workpad to look the way you want, add your own images. + The default Elastic logo image appears on the page. -. To replace the Elastic logo with your own image, select the image, then use the editor. - +. To add your own image, click the Elastic logo, then drag the image file to the *Select or drag and drop an image* field. ++ [role="screenshot"] image::images/canvas-image-element.png[Image showing how to add the image element] diff --git a/docs/canvas/images/canvas-add-image.gif b/docs/canvas/images/canvas-add-image.gif deleted file mode 100644 index 994ec6e1b4f28..0000000000000 Binary files a/docs/canvas/images/canvas-add-image.gif and /dev/null differ diff --git a/docs/canvas/images/canvas-add-pages.gif b/docs/canvas/images/canvas-add-pages.gif index c6e09d6f386ae..0b001c5f8b10c 100644 Binary files a/docs/canvas/images/canvas-add-pages.gif and b/docs/canvas/images/canvas-add-pages.gif differ diff --git a/docs/canvas/images/canvas-autoplay-interval.png b/docs/canvas/images/canvas-autoplay-interval.png index a7b1251efc808..52837a4feb047 100644 Binary files a/docs/canvas/images/canvas-autoplay-interval.png and b/docs/canvas/images/canvas-autoplay-interval.png differ diff --git a/docs/canvas/images/canvas-background-color-picker.png b/docs/canvas/images/canvas-background-color-picker.png deleted file mode 100644 index ec38b5c1c5f7e..0000000000000 Binary files a/docs/canvas/images/canvas-background-color-picker.png and /dev/null differ diff --git a/docs/canvas/images/canvas-chart-element.png b/docs/canvas/images/canvas-chart-element.png index bf5e04bf89af5..c6942dfb61a3f 100644 Binary files a/docs/canvas/images/canvas-chart-element.png and b/docs/canvas/images/canvas-chart-element.png differ diff --git a/docs/canvas/images/canvas-create-URL.gif b/docs/canvas/images/canvas-create-URL.gif deleted file mode 100644 index 60d69cdd599a3..0000000000000 Binary files a/docs/canvas/images/canvas-create-URL.gif and /dev/null differ diff --git a/docs/canvas/images/canvas-element-select.gif b/docs/canvas/images/canvas-element-select.gif index 1bfd1132f25c7..3bcba5c64c93d 100644 Binary files a/docs/canvas/images/canvas-element-select.gif and b/docs/canvas/images/canvas-element-select.gif differ diff --git a/docs/canvas/images/canvas-embed_workpad.gif b/docs/canvas/images/canvas-embed_workpad.gif deleted file mode 100644 index 1cda5b572acef..0000000000000 Binary files a/docs/canvas/images/canvas-embed_workpad.gif and /dev/null differ diff --git a/docs/canvas/images/canvas-export-workpad.png b/docs/canvas/images/canvas-export-workpad.png deleted file mode 100644 index 213bbaa5a26d3..0000000000000 Binary files a/docs/canvas/images/canvas-export-workpad.png and /dev/null differ diff --git a/docs/canvas/images/canvas-fullscreen.png b/docs/canvas/images/canvas-fullscreen.png deleted file mode 100644 index b8a816d290396..0000000000000 Binary files a/docs/canvas/images/canvas-fullscreen.png and /dev/null differ diff --git a/docs/canvas/images/canvas-generate-pdf.gif b/docs/canvas/images/canvas-generate-pdf.gif deleted file mode 100644 index 24711d01fbe0c..0000000000000 Binary files a/docs/canvas/images/canvas-generate-pdf.gif and /dev/null differ diff --git a/docs/canvas/images/canvas-gs-example.png b/docs/canvas/images/canvas-gs-example.png deleted file mode 100644 index bae32ef96a93f..0000000000000 Binary files a/docs/canvas/images/canvas-gs-example.png and /dev/null differ diff --git a/docs/canvas/images/canvas-image-element.png b/docs/canvas/images/canvas-image-element.png index 13c9090e77c76..ef1ab3bb3f105 100644 Binary files a/docs/canvas/images/canvas-image-element.png and b/docs/canvas/images/canvas-image-element.png differ diff --git a/docs/canvas/images/canvas-map-embed.gif b/docs/canvas/images/canvas-map-embed.gif deleted file mode 100644 index c6ba5c29df42a..0000000000000 Binary files a/docs/canvas/images/canvas-map-embed.gif and /dev/null differ diff --git a/docs/canvas/images/canvas-metric-element.png b/docs/canvas/images/canvas-metric-element.png index 03871dcc81862..8dcdb8d42cc9d 100644 Binary files a/docs/canvas/images/canvas-metric-element.png and b/docs/canvas/images/canvas-metric-element.png differ diff --git a/docs/canvas/images/canvas-refresh-data.png b/docs/canvas/images/canvas-refresh-data.png index 7a71686f04491..6f051274245c8 100644 Binary files a/docs/canvas/images/canvas-refresh-data.png and b/docs/canvas/images/canvas-refresh-data.png differ diff --git a/docs/canvas/images/canvas-refresh-interval.png b/docs/canvas/images/canvas-refresh-interval.png deleted file mode 100644 index c097d950a7ec7..0000000000000 Binary files a/docs/canvas/images/canvas-refresh-interval.png and /dev/null differ diff --git a/docs/canvas/images/canvas-timefilter-element.png b/docs/canvas/images/canvas-timefilter-element.png index e210b0b3288c6..b79adbfeea094 100644 Binary files a/docs/canvas/images/canvas-timefilter-element.png and b/docs/canvas/images/canvas-timefilter-element.png differ diff --git a/docs/canvas/images/canvas-zoom-controls.png b/docs/canvas/images/canvas-zoom-controls.png deleted file mode 100644 index 1407ca3cd8627..0000000000000 Binary files a/docs/canvas/images/canvas-zoom-controls.png and /dev/null differ diff --git a/docs/canvas/images/canvas_element_options.png b/docs/canvas/images/canvas_element_options.png deleted file mode 100644 index 41457bab4ff36..0000000000000 Binary files a/docs/canvas/images/canvas_element_options.png and /dev/null differ diff --git a/docs/canvas/images/canvas_save_element.png b/docs/canvas/images/canvas_save_element.png deleted file mode 100644 index 8a601efab874a..0000000000000 Binary files a/docs/canvas/images/canvas_save_element.png and /dev/null differ diff --git a/docs/canvas/images/clone_element.gif b/docs/canvas/images/clone_element.gif deleted file mode 100644 index ef8f44223d240..0000000000000 Binary files a/docs/canvas/images/clone_element.gif and /dev/null differ diff --git a/docs/dev-tools/console/console.asciidoc b/docs/dev-tools/console/console.asciidoc index 731290fea22d0..e1cd156e6a9e4 100644 --- a/docs/dev-tools/console/console.asciidoc +++ b/docs/dev-tools/console/console.asciidoc @@ -1,7 +1,7 @@ [[console-kibana]] -== Console +== Run {es} API requests -*Console* enables you to interact with the REST API of {es}. You can: +Interact with the REST API of {es} with *Console*. You can: * Send requests to {es} and view the responses * View API documentation diff --git a/docs/dev-tools/painlesslab/index.asciidoc b/docs/dev-tools/painlesslab/index.asciidoc index 4077ffe87ca1a..387c0522dd1ca 100644 --- a/docs/dev-tools/painlesslab/index.asciidoc +++ b/docs/dev-tools/painlesslab/index.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[painlesslab]] -== Painless Lab +== Debug Painless scripts beta::[] diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index d2b5f54a59383..ba594b1f312e9 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -80,6 +80,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |Expression Image plugin adds an image renderer to the expression plugin. The renderer will display the given image. +|{kib-repo}blob/{branch}/src/plugins/expression_metric/README.md[expressionMetric] +|Expression Metric plugin adds a metric renderer and function to the expression plugin. + + |{kib-repo}blob/{branch}/src/plugins/expression_repeat_image/README.md[expressionRepeatImage] |Expression Repeat Image plugin adds a repeatImage function to the expression plugin and an associated renderer. The renderer will display the given image in mutliple instances. diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md index 7592b8486d950..e5f08213da510 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md @@ -19,4 +19,5 @@ export interface DeprecationsDetails | [documentationUrl](./kibana-plugin-core-server.deprecationsdetails.documentationurl.md) | string | | | [level](./kibana-plugin-core-server.deprecationsdetails.level.md) | 'warning' | 'critical' | 'fetch_error' | levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain. | | [message](./kibana-plugin-core-server.deprecationsdetails.message.md) | string | | +| [requireRestart](./kibana-plugin-core-server.deprecationsdetails.requirerestart.md) | boolean | | diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.requirerestart.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.requirerestart.md new file mode 100644 index 0000000000000..52c0fcf1c3001 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.requirerestart.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [requireRestart](./kibana-plugin-core-server.deprecationsdetails.requirerestart.md) + +## DeprecationsDetails.requireRestart property + +Signature: + +```typescript +requireRestart?: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.md b/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.md index 517e8f3ffda1f..c21eb1110ed8e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.md @@ -18,6 +18,6 @@ export interface KibanaExecutionContext | [description](./kibana-plugin-core-server.kibanaexecutioncontext.description.md) | string | human readable description. For example, a vis title, action name | | [id](./kibana-plugin-core-server.kibanaexecutioncontext.id.md) | string | unique value to identify the source | | [name](./kibana-plugin-core-server.kibanaexecutioncontext.name.md) | string | public name of a user-facing feature | -| [type](./kibana-plugin-core-server.kibanaexecutioncontext.type.md) | string | Kibana application initated an operation. Can be narrowed to an enum later. | +| [type](./kibana-plugin-core-server.kibanaexecutioncontext.type.md) | string | Kibana application initated an operation. | | [url](./kibana-plugin-core-server.kibanaexecutioncontext.url.md) | string | in browser - url to navigate to a current page, on server - endpoint path, for task: task SO url | diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.type.md b/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.type.md index 534b0cdea1753..6941bb150efdd 100644 --- a/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.type.md +++ b/docs/development/core/server/kibana-plugin-core-server.kibanaexecutioncontext.type.md @@ -4,7 +4,7 @@ ## KibanaExecutionContext.type property -Kibana application initated an operation. Can be narrowed to an enum later. +Kibana application initated an operation. Signature: diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getallmigrations.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getallmigrations.md new file mode 100644 index 0000000000000..6613afb98efc2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getallmigrations.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [getAllMigrations](./kibana-plugin-plugins-expressions-public.executor.getallmigrations.md) + +## Executor.getAllMigrations() method + +Signature: + +```typescript +getAllMigrations(): MigrateFunctionsObject; +``` +Returns: + +`MigrateFunctionsObject` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md index 6835188c2fb04..61caebbd36368 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md @@ -34,12 +34,13 @@ export declare class Executor = Record -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [migrate](./kibana-plugin-plugins-expressions-public.executor.migrate.md) +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [migrateToLatest](./kibana-plugin-plugins-expressions-public.executor.migratetolatest.md) -## Executor.migrate() method +## Executor.migrateToLatest() method Signature: ```typescript -migrate(ast: SerializableState, version: string): ExpressionAstExpression; +migrateToLatest(state: VersionedState): ExpressionAstExpression; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| ast | SerializableState | | -| version | string | | +| state | VersionedState | | Returns: diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md new file mode 100644 index 0000000000000..b337d0dc21b4e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getAllMigrations](./kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md) + +## ExpressionsService.getAllMigrations property + +gets an object with semver mapped to a migration function + +Signature: + +```typescript +getAllMigrations: () => import("../../../kibana_utils/common").MigrateFunctionsObject; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md index 9afd603bc4869..cde8c7c1a8f24 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md @@ -32,6 +32,7 @@ export declare class ExpressionsService implements PersistableStateServiceExecutor | | | [extract](./kibana-plugin-plugins-expressions-public.expressionsservice.extract.md) | | (state: ExpressionAstExpression) => {
state: ExpressionAstExpression;
references: SavedObjectReference[];
} | Extracts saved object references from expression AST | | [fork](./kibana-plugin-plugins-expressions-public.expressionsservice.fork.md) | | () => ExpressionsService | | +| [getAllMigrations](./kibana-plugin-plugins-expressions-public.expressionsservice.getallmigrations.md) | | () => import("../../../kibana_utils/common").MigrateFunctionsObject | gets an object with semver mapped to a migration function | | [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md) | | ExpressionsServiceStart['getFunction'] | | | [getFunctions](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md) | | () => ReturnType<Executor['getFunctions']> | Returns POJO map of all registered expression functions, where keys are names of the functions and values are ExpressionFunction instances. | | [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md) | | ExpressionsServiceStart['getRenderer'] | | @@ -39,7 +40,7 @@ export declare class ExpressionsService implements PersistableStateServiceExpressionsServiceStart['getType'] | | | [getTypes](./kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md) | | () => ReturnType<Executor['getTypes']> | Returns POJO map of all registered expression types, where keys are names of the types and values are ExpressionType instances. | | [inject](./kibana-plugin-plugins-expressions-public.expressionsservice.inject.md) | | (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression | Injects saved object references into expression AST | -| [migrate](./kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md) | | (state: SerializableState, version: string) => ExpressionAstExpression | Runs the migration (if it exists) for specified version. This will run a single migration step (ie from 7.10.0 to 7.10.1) | +| [migrateToLatest](./kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md) | | (state: VersionedState) => ExpressionAstExpression | migrates an old expression to latest version | | [registerFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md) | | (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void | Register an expression function, which will be possible to execute as part of the expression pipeline.Below we register a function which simply sleeps for given number of milliseconds to delay the execution and outputs its input as-is. ```ts expressions.registerFunction({ diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md deleted file mode 100644 index d1f24bfcfc0bb..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [migrate](./kibana-plugin-plugins-expressions-public.expressionsservice.migrate.md) - -## ExpressionsService.migrate property - -Runs the migration (if it exists) for specified version. This will run a single migration step (ie from 7.10.0 to 7.10.1) - -Signature: - -```typescript -readonly migrate: (state: SerializableState, version: string) => ExpressionAstExpression; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md new file mode 100644 index 0000000000000..55efb8d5a8af3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [migrateToLatest](./kibana-plugin-plugins-expressions-public.expressionsservice.migratetolatest.md) + +## ExpressionsService.migrateToLatest property + +migrates an old expression to latest version + +Signature: + +```typescript +migrateToLatest: (state: VersionedState) => ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md index dcdbd663f84e4..a228628fece0f 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md @@ -28,7 +28,7 @@ export interface IExpressionLoaderParams | [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) | SerializableState | | | [searchSessionId](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchsessionid.md) | string | | | [syncColors](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md) | boolean | | -| [throttle](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md) | number | Throttling of partial results in milliseconds. By default, throttling is disabled. | +| [throttle](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md) | number | Throttling of partial results in milliseconds. 0 is disabling the throttling. By default, it equals 1000. | | [uiState](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md) | unknown | | | [variables](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md) | Record<string, any> | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md index 3383bce879776..54949bbbd5dd6 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.throttle.md @@ -4,7 +4,7 @@ ## IExpressionLoaderParams.throttle property -Throttling of partial results in milliseconds. By default, throttling is disabled. +Throttling of partial results in milliseconds. 0 is disabling the throttling. By default, it equals 1000. Signature: diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getallmigrations.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getallmigrations.md new file mode 100644 index 0000000000000..f397f3a9869b8 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getallmigrations.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [getAllMigrations](./kibana-plugin-plugins-expressions-server.executor.getallmigrations.md) + +## Executor.getAllMigrations() method + +Signature: + +```typescript +getAllMigrations(): MigrateFunctionsObject; +``` +Returns: + +`MigrateFunctionsObject` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md index 48002a9f986df..00b4300990ca8 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md @@ -34,12 +34,13 @@ export declare class Executor = Record -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [migrate](./kibana-plugin-plugins-expressions-server.executor.migrate.md) +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [migrateToLatest](./kibana-plugin-plugins-expressions-server.executor.migratetolatest.md) -## Executor.migrate() method +## Executor.migrateToLatest() method Signature: ```typescript -migrate(ast: SerializableState, version: string): ExpressionAstExpression; +migrateToLatest(state: VersionedState): ExpressionAstExpression; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| ast | SerializableState | | -| version | string | | +| state | VersionedState | | Returns: diff --git a/docs/management/field-formatters/color-formatter.asciidoc b/docs/management/field-formatters/color-formatter.asciidoc index 488fb37153799..855b7d804c7fb 100644 --- a/docs/management/field-formatters/color-formatter.asciidoc +++ b/docs/management/field-formatters/color-formatter.asciidoc @@ -1,5 +1,3 @@ The *Color* field formatter enables you to specify colors with ranges of values for a number field. When you select the *Color* formatter, click *Add Color*, then specify the *Range*, *Text color*, and *Background color*. - -image::images/colorformatter.png[] diff --git a/docs/settings/fleet-settings.asciidoc b/docs/settings/fleet-settings.asciidoc index cb80165e70990..3ae1c9df616b0 100644 --- a/docs/settings/fleet-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -5,6 +5,11 @@ {fleet} settings ++++ +[NOTE] +==== +In {ecloud}, {fleet} flags are already configured. +==== + You can configure `xpack.fleet` settings in your `kibana.yml`. By default, {fleet} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. @@ -28,7 +33,10 @@ See the {fleet-guide}/index.html[{fleet}] docs for more information. [cols="2*<"] |=== | `xpack.fleet.registryUrl` - | The address to use to reach {package-manager} registry. + | The address to use to reach the {package-manager} registry. +| `xpack.fleet.registryProxyUrl` + | The proxy address to use to reach the {package-manager} registry. + |=== ==== {fleet} settings @@ -39,9 +47,86 @@ See the {fleet-guide}/index.html[{fleet}] docs for more information. | Hostnames used by {agent} for accessing {fleet-server}. | `xpack.fleet.agents.elasticsearch.hosts` | Hostnames used by {agent} for accessing {es}. +| `xpack.fleet.agents.elasticsearch.ca_sha256` + | Hash pin used for certificate verification. The pin is a base64-encoded + string of the SHA-256 fingerprint. |=== -[NOTE] -==== -In {ecloud}, {fleet} flags are already configured. -==== + +==== Preconfiguration settings (for advanced use cases) + +Use these settings to pre-define integrations and agent policies that you +want {fleet} to load up by default. + +[cols="2*> to a non-loopback address. -. To allow remote users to connect to {kib}, set the parameter `server.host` in kibana.yml to a non-loopback address. +. Log on to your account. -. On the home page, click *{kib}*. -+ -To make the {kib} page your landing page, click *Make this my landing page*. +. Go to the home page, then click *{kib}*. + +. To make the {kib} page your landing page, click *Make this my landing page*. [float] [[status]] === Check the {kib} status -To view the {kib} status page, use the status endpoint. For example, `localhost:5601/status`. The status page displays -information about the server resource usage and installed plugins. +The status page displays information about the server resource usage and installed plugins. + +To view the {kib} status page, use the status endpoint. For example, `localhost:5601/status`. [role="screenshot"] -image::images/kibana-status-page-7_5_0.png[] +image::images/kibana-status-page-7_14_0.png[Kibana server status page] For JSON-formatted server status details, use the `localhost:5601/api/status` API endpoint. diff --git a/docs/setup/configuring-reporting.asciidoc b/docs/setup/configuring-reporting.asciidoc index af4fc14448ac5..0dba7befa2931 100644 --- a/docs/setup/configuring-reporting.asciidoc +++ b/docs/setup/configuring-reporting.asciidoc @@ -42,14 +42,10 @@ To troubleshoot the problem, start the {kib} server with environment variables t [[grant-user-access]] === Grant users access to reporting -When security is enabled, access to the {report-features} is controlled by roles and privileges. +When security is enabled, access to the {report-features} is controlled by roles and <>. With privileges, you can define custom roles that grant *Reporting* privileges as sub-features of {kib} applications. To grant users permission to generate reports and view their reports in *Reporting*, create and assign the reporting role. [[reporting-app-users]] -In 7.12.0 and earlier, you grant access to the {report-features} by assigning users the `reporting_user` role in {es}. - -In 7.14.0 and later, you configure *Reporting* to use <>. By using {kib} privileges, you can define custom roles that grant *Reporting* privileges as sub-features of {kib} applications. - -To grant users permission to generate reports and view their reports in *Reporting*, create and assign the reporting role. +NOTE: In 7.12.0 and earlier, you grant access to the {report-features} by assigning users the `reporting_user` role in {es}. . Create the reporting role. @@ -59,6 +55,7 @@ To grant users permission to generate reports and view their reports in *Reporti . Specify the role settings. + .. Enter the *Role name*. For example, `custom_reporting_user`. .. Specify the *Indices* and *Privileges*. @@ -66,22 +63,18 @@ To grant users permission to generate reports and view their reports in *Reporti Access to data is an index-level privilege. For each index that contains the data you want to include in reports, add a line, then give each index `read` and `view_index_metadata` privileges. + For more information, refer to {ref}/security-privileges.html[Security privileges]. -+ -[role="screenshot"] -image::user/security/images/reporting-privileges-example.png["Reporting privileges"] . Add the {kib} privileges. .. Click *Add Kibana privilege*. -.. Select one or more *Spaces* that you want to grant reporting privileges to. +.. Select one or more *Spaces*. .. Click *Customize*, then click *Analytics*. -.. Next to each application you want to grant reporting privileges to, click *All*. +.. Next to the applications you want to grant reporting privileges, click *All*. + -[role="screenshot"] -image::user/security/images/reporting-custom-role.png["Reporting custom role"] +If the *Reporting* option is unavailable, contact your administrator, or <>. .. Click *Add {kib} privilege*. diff --git a/docs/setup/images/kibana-status-page-7_14_0.png b/docs/setup/images/kibana-status-page-7_14_0.png new file mode 100644 index 0000000000000..db172d87eedb5 Binary files /dev/null and b/docs/setup/images/kibana-status-page-7_14_0.png differ diff --git a/docs/user/canvas.asciidoc b/docs/user/canvas.asciidoc index 08462e0f0ed24..1cd8eacc456c7 100644 --- a/docs/user/canvas.asciidoc +++ b/docs/user/canvas.asciidoc @@ -67,9 +67,6 @@ To use the background colors, images, and data of your choice, start with a blan .. In the *Width* and *Height* fields, specify the size, or select one of default layouts. .. Click the *Background* color picker, then select the color for your workpad. -+ -[role="screenshot"] -image::images/canvas-background-color-picker.png[Canvas color picker] [float] [[create-workpads-from-templates]] @@ -133,9 +130,6 @@ Each element can display a different data source, and pages and workpads often c . To save, use the following options: * To save a single element, select the element, then click *Edit > Save as new element*. -+ -[role="screenshot"] -image::images/canvas_save_element.png[] * To save a group of elements, press and hold Shift, select the elements you want to save, then click *Edit > Save as new element*. @@ -150,9 +144,6 @@ Add a panel that you saved in *Visualize Library* to your workpad. . Click *Add element > Add from {kib}*. . Select the panel you want to add. -+ -[role="screenshot"] -image::images/canvas-map-embed.gif[] . To use the customization options, open the panel menu, then select one of the following options: @@ -177,9 +168,6 @@ To personalize your workpad, add your own logos and graphics. . On the *Manage workpad assets* window, drag and drop your images. . To add the image to the workpad, click the *Create image element* icon. -+ -[role="screenshot"] -image::images/canvas-add-image.gif[Add image to Canvas] [float] [[add-more-pages]] diff --git a/docs/user/reporting/images/shareable-container.png b/docs/user/reporting/images/shareable-container.png index 829fe15706a52..2bf3812e94c2d 100644 Binary files a/docs/user/reporting/images/shareable-container.png and b/docs/user/reporting/images/shareable-container.png differ diff --git a/docs/user/reporting/index.asciidoc b/docs/user/reporting/index.asciidoc index 457be5b038c12..cb999ed189d77 100644 --- a/docs/user/reporting/index.asciidoc +++ b/docs/user/reporting/index.asciidoc @@ -17,7 +17,7 @@ You access the options from the *Share* menu in the toolbar. The sharing options * *PNG Reports* — Generate and download a PNG file of a dashboard or visualization. -* *CSV Reports* — Generate and download a CSV file of a saved search. +* *CSV Reports* — Generate and download a CSV file of a *Discover* saved search. * *Permalinks* — Share a direct link to a *Discover* saved search, dashboard, or visualization. @@ -90,7 +90,7 @@ Share a direct link to a saved search, dashboard, or visualization. To access th [role="screenshot"] image::images/permalink-public-url.png[Permalink share menu with Public URL option highlighted] + -NOTE: *Public URL* is available only when anonymous access is configured and your anonymous service account has privileges to access what you want to share. +NOTE: *Public URL* is available only when anonymous access is configured and your anonymous service account has privileges to access what you want to share. For more information, refer to <>. . Click *Copy link*. @@ -156,7 +156,7 @@ Some users might not have access to the dashboard or visualization. For more inf [role="screenshot"] image::images/embed-code-public-url.png[Embed code share menu with Public URL option highlighted] + -NOTE: *Public URL* is available only when anonymous access is configured and your anonymous service account has privileges to access what you want to embed. +NOTE: *Public URL* is available only when anonymous access is configured and your anonymous service account has privileges to access what you want to embed. For more information, refer to <>. . Click *Copy iFrame code*. diff --git a/docs/user/security/images/reporting-privileges-example.png b/docs/user/security/images/reporting-privileges-example.png index d108fe6634fa2..49e58b9c1a008 100644 Binary files a/docs/user/security/images/reporting-privileges-example.png and b/docs/user/security/images/reporting-privileges-example.png differ diff --git a/examples/index_pattern_field_editor_example/kibana.json b/examples/index_pattern_field_editor_example/kibana.json index c522e6698ac3d..f28223ac7d24b 100644 --- a/examples/index_pattern_field_editor_example/kibana.json +++ b/examples/index_pattern_field_editor_example/kibana.json @@ -6,5 +6,10 @@ "ui": true, "requiredPlugins": ["data", "indexPatternFieldEditor", "developerExamples"], "optionalPlugins": [], - "requiredBundles": [] + "requiredBundles": [], + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "Index pattern field editor example app" } diff --git a/package.json b/package.json index f7856b9f92e74..3a16330bdf970 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,6 @@ "@elastic/request-crypto": "1.1.4", "@elastic/safer-lodash-set": "link:bazel-bin/packages/elastic-safer-lodash-set", "@elastic/search-ui-app-search-connector": "^1.6.0", - "@elastic/ui-ace": "0.2.3", "@emotion/react": "^11.4.0", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.1", @@ -194,7 +193,6 @@ "angular-route": "^1.8.0", "angular-sanitize": "^1.8.0", "angular-sortable-view": "^0.0.17", - "angular-ui-ace": "0.2.3", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.2.0", "axios": "^0.21.1", diff --git a/packages/kbn-analytics/src/application_usage_tracker.ts b/packages/kbn-analytics/src/application_usage_tracker.ts index ddc59fe1c53b3..b1cc47334541a 100644 --- a/packages/kbn-analytics/src/application_usage_tracker.ts +++ b/packages/kbn-analytics/src/application_usage_tracker.ts @@ -156,8 +156,9 @@ export class ApplicationUsageTracker { const appKey = this.createKey(this.currentAppId, viewId); const serializedKey = ApplicationUsageTracker.serializeKey(appKey); const appViewMetric = this.trackedApplicationViews[serializedKey]; - this.sendMetricsToReporter([appViewMetric]); - - delete this.trackedApplicationViews[serializedKey]; + if (appViewMetric) { + this.sendMetricsToReporter([appViewMetric]); + delete this.trackedApplicationViews[serializedKey]; + } } } diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ef6ff52a6c8b8..b135f2d65c6fa 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -115,5 +115,6 @@ pageLoadAssetSize: expressionError: 22127 expressionRepeatImage: 22341 expressionImage: 19288 + expressionMetric: 22238 expressionShape: 30033 userSetup: 18532 diff --git a/src/core/server/deprecations/deprecations_service.test.ts b/src/core/server/deprecations/deprecations_service.test.ts index d1ed7a83402cb..a0235b478b8f0 100644 --- a/src/core/server/deprecations/deprecations_service.test.ts +++ b/src/core/server/deprecations/deprecations_service.test.ts @@ -91,6 +91,7 @@ describe('DeprecationsService', () => { "documentationUrl": "testDocUrl", "level": "critical", "message": "testMessage", + "requireRestart": true, }, ] `); diff --git a/src/core/server/deprecations/deprecations_service.ts b/src/core/server/deprecations/deprecations_service.ts index ede7f859ffd0d..e65752c9286ad 100644 --- a/src/core/server/deprecations/deprecations_service.ts +++ b/src/core/server/deprecations/deprecations_service.ts @@ -156,6 +156,7 @@ export class DeprecationsService implements CoreService { expect.anything() ); }); + + it('does not increment counter when incrementBy is 0', async () => { + await incrementCounterSuccess(type, id, [{ fieldName: counterFields[0], incrementBy: 0 }]); + + expect(client.update).toBeCalledTimes(1); + expect(client.update).toBeCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + script: expect.objectContaining({ + params: expect.objectContaining({ + counterFieldNames: [counterFields[0]], + counts: [0], + }), + }), + }), + }), + expect.anything() + ); + }); }); }); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 986467c917dd2..6899f8613b07f 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -1705,8 +1705,20 @@ export class SavedObjectsRepository { } = options; const normalizedCounterFields = counterFields.map((counterField) => { - const fieldName = typeof counterField === 'string' ? counterField : counterField.fieldName; - const incrementBy = typeof counterField === 'string' ? 1 : counterField.incrementBy || 1; + /** + * no counterField configs provided, instead a field name string was passed. + * ie `incrementCounter(so_type, id, ['my_field_name'])` + * Using the default of incrementing by 1 + */ + if (typeof counterField === 'string') { + return { + fieldName: counterField, + incrementBy: initialize ? 0 : 1, + }; + } + + const { incrementBy = 1, fieldName } = counterField; + return { fieldName, incrementBy: initialize ? 0 : incrementBy, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 52548c760e30b..8b6574ccc8e06 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -915,6 +915,8 @@ export interface DeprecationsDetails { level: 'warning' | 'critical' | 'fetch_error'; // (undocumented) message: string; + // (undocumented) + requireRestart?: boolean; } // @public diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index b27ff86631725..965a716098f33 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -19,6 +19,7 @@ export const storybookAliases = { embeddable: 'src/plugins/embeddable/.storybook', expression_error: 'src/plugins/expression_error/.storybook', expression_image: 'src/plugins/expression_image/.storybook', + expression_metric: 'src/plugins/expression_metric/.storybook', expression_repeat_image: 'src/plugins/expression_repeat_image/.storybook', expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', diff --git a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts index 11f6954af97b0..8dad40d373f31 100644 --- a/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/apps/main/services/discover_state.test.ts @@ -202,5 +202,11 @@ describe('createSearchSessionRestorationDataProvider', () => { expect(initialState.timeRange).toBe(relativeTime); expect(restoreState.timeRange).toBe(absoluteTime); }); + + test('restoreState has paused autoRefresh', async () => { + const { initialState, restoreState } = await searchSessionInfoProvider.getUrlGeneratorData(); + expect(initialState.refreshInterval).toBe(undefined); + expect(restoreState.refreshInterval?.pause).toBe(true); + }); }); }); diff --git a/src/plugins/discover/public/application/apps/main/services/discover_state.ts b/src/plugins/discover/public/application/apps/main/services/discover_state.ts index 03705bac8f16c..e57e3bb029f31 100644 --- a/src/plugins/discover/public/application/apps/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/apps/main/services/discover_state.ts @@ -392,6 +392,12 @@ function createUrlGeneratorState({ sort: appState.sort, savedQuery: appState.savedQuery, interval: appState.interval, + refreshInterval: shouldRestoreSearchSession + ? { + pause: true, // force pause refresh interval when restoring a session + value: 0, + } + : undefined, useHash: false, }; } diff --git a/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap b/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap new file mode 100644 index 0000000000000..6ef25188283e5 --- /dev/null +++ b/src/plugins/embeddable/public/__snapshots__/plugin.test.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`embeddable factory migrateToLatest returns list of all migrations 1`] = ` +Object { + "7.11.0": [Function], + "7.12.0": [Function], +} +`; diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index b93dc02ebb5a8..a616b559d3236 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -165,6 +165,19 @@ describe('embeddable factory', () => { start.getAllMigrations!()['7.11.0']!(containerState); expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); }); + + test('migrateToLatest returns list of all migrations', () => { + const migrations = start.getAllMigrations(); + expect(migrations).toMatchSnapshot(); + }); + + test('migrateToLatest calls correct migrate functions', () => { + start.migrateToLatest!({ + state: embeddableState, + version: '7.11.0', + }); + expect(embeddableFactory.migrations['7.11.0']).toBeCalledWith(embeddableState); + }); }); describe('embeddable enhancements', () => { diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 62ec9e15f564c..cfb16da7b46b8 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -39,7 +39,11 @@ import { import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; import { EmbeddableStateTransfer } from './lib/state_transfer'; import { Storage } from '../../kibana_utils/public'; -import { PersistableStateService, SerializableState } from '../../kibana_utils/common'; +import { + migrateToLatest, + PersistableStateService, + SerializableState, +} from '../../kibana_utils/common'; import { ATTRIBUTE_SERVICE_KEY, AttributeService } from './lib/attribute_service'; import { AttributeServiceOptions } from './lib/attribute_service/attribute_service'; import { EmbeddableStateWithType } from '../common/types'; @@ -181,6 +185,13 @@ export class EmbeddablePublicPlugin implements Plugin + getAllMigrations( + Array.from(this.embeddableFactories.values()), + Array.from(this.enhancements.values()), + getMigrateFunction(commonContract) + ); + return { getEmbeddableFactory: this.getEmbeddableFactory, getEmbeddableFactories: this.getEmbeddableFactories, @@ -206,12 +217,10 @@ export class EmbeddablePublicPlugin implements Plugin - getAllMigrations( - Array.from(this.embeddableFactories.values()), - Array.from(this.enhancements.values()), - getMigrateFunction(commonContract) - ), + getAllMigrations: getAllMigrationsFn, + migrateToLatest: (state) => { + return migrateToLatest(getAllMigrationsFn(), state) as EmbeddableStateWithType; + }, }; } diff --git a/src/plugins/expression_image/__fixtures__/function_specs.ts b/src/plugins/expression_image/__fixtures__/function_specs.ts new file mode 100644 index 0000000000000..2117a8fc4b726 --- /dev/null +++ b/src/plugins/expression_image/__fixtures__/function_specs.ts @@ -0,0 +1,12 @@ +/* + * 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 { imageFunction } from '../common/expression_functions'; +import { ExpressionFunction } from '../../../../src/plugins/expressions'; + +export const functionSpecs = [imageFunction].map((fn) => new ExpressionFunction(fn())); diff --git a/src/plugins/expression_image/__fixtures__/index.ts b/src/plugins/expression_image/__fixtures__/index.ts index 279e8d87446bc..b1087597ec172 100644 --- a/src/plugins/expression_image/__fixtures__/index.ts +++ b/src/plugins/expression_image/__fixtures__/index.ts @@ -6,4 +6,5 @@ * Side Public License, v 1. */ +export { functionSpecs } from './function_specs'; export { imageFunction } from '../common/expression_functions'; diff --git a/src/plugins/expression_metric/.storybook/main.js b/src/plugins/expression_metric/.storybook/main.js new file mode 100644 index 0000000000000..742239e638b8a --- /dev/null +++ b/src/plugins/expression_metric/.storybook/main.js @@ -0,0 +1,10 @@ +/* + * 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. + */ + +// eslint-disable-next-line import/no-commonjs +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/expression_metric/README.md b/src/plugins/expression_metric/README.md new file mode 100755 index 0000000000000..ae02ebd51256e --- /dev/null +++ b/src/plugins/expression_metric/README.md @@ -0,0 +1,9 @@ +# expressionMetric + +Expression Metric plugin adds a `metric` renderer and function to the expression plugin. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/expression_metric/__fixtures__/function_specs.ts b/src/plugins/expression_metric/__fixtures__/function_specs.ts new file mode 100644 index 0000000000000..3d36840b47e0d --- /dev/null +++ b/src/plugins/expression_metric/__fixtures__/function_specs.ts @@ -0,0 +1,12 @@ +/* + * 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 { metricFunction } from '../common/expression_functions'; +import { ExpressionFunction } from '../../../../src/plugins/expressions'; + +export const functionSpecs = [metricFunction].map((fn) => new ExpressionFunction(fn())); diff --git a/src/plugins/expression_metric/__fixtures__/index.ts b/src/plugins/expression_metric/__fixtures__/index.ts new file mode 100644 index 0000000000000..048c916b21b25 --- /dev/null +++ b/src/plugins/expression_metric/__fixtures__/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 * from './function_specs'; diff --git a/src/plugins/expression_metric/common/constants.ts b/src/plugins/expression_metric/common/constants.ts new file mode 100644 index 0000000000000..62753ac728c41 --- /dev/null +++ b/src/plugins/expression_metric/common/constants.ts @@ -0,0 +1,15 @@ +/* + * 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 PLUGIN_ID = 'expressionMetric'; +export const PLUGIN_NAME = 'expressionMetric'; + +export const FONT_FAMILY = '`font-family`'; +export const FONT_WEIGHT = '`font-weight`'; +export const CSS = 'CSS'; +export const NUMERALJS = 'Numeral pattern'; diff --git a/src/plugins/expression_metric/common/expression_functions/index.ts b/src/plugins/expression_metric/common/expression_functions/index.ts new file mode 100644 index 0000000000000..53dd9c7311684 --- /dev/null +++ b/src/plugins/expression_metric/common/expression_functions/index.ts @@ -0,0 +1,13 @@ +/* + * 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 { metricFunction } from './metric_function'; + +export const functions = [metricFunction]; + +export { metricFunction }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/metric.test.js b/src/plugins/expression_metric/common/expression_functions/metric_function.test.ts similarity index 57% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/metric.test.js rename to src/plugins/expression_metric/common/expression_functions/metric_function.test.ts index 3f2d0ad2cb76e..88249728526d0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/metric.test.js +++ b/src/plugins/expression_metric/common/expression_functions/metric_function.test.ts @@ -1,39 +1,44 @@ /* * 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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib'; -import { fontStyle } from './__fixtures__/test_styles'; -import { metric } from './metric'; +import { ExecutionContext } from 'src/plugins/expressions'; +import { functionWrapper, fontStyle } from '../../../presentation_util/common/lib'; +import { metricFunction } from './metric_function'; describe('metric', () => { - const fn = functionWrapper(metric); + const fn = functionWrapper(metricFunction); it('returns a render as metric', () => { - const result = fn(null); + const result = fn(null, {}, {} as ExecutionContext); expect(result).toHaveProperty('type', 'render'); expect(result).toHaveProperty('as', 'metric'); }); it('sets the metric to context', () => { - const result = fn('2'); + const result = fn('2', {}, {} as ExecutionContext); expect(result.value).toHaveProperty('metric', '2'); }); it(`defaults metric to '?' when context is missing`, () => { - const result = fn(null); + const result = fn(null, {}, {} as ExecutionContext); expect(result.value).toHaveProperty('metric', '?'); }); describe('args', () => { describe('label', () => { it('sets the label of the metric', () => { - const result = fn(null, { - label: 'My Label', - }); + const result = fn( + null, + { + label: 'My Label', + }, + {} as ExecutionContext + ); expect(result.value).toHaveProperty('label', 'My Label'); }); @@ -41,9 +46,13 @@ describe('metric', () => { describe('metricFont', () => { it('sets the font style for the metric', () => { - const result = fn(null, { - metricFont: fontStyle, - }); + const result = fn( + null, + { + metricFont: fontStyle, + }, + {} as ExecutionContext + ); expect(result.value).toHaveProperty('metricFont', fontStyle); }); @@ -54,9 +63,13 @@ describe('metric', () => { describe('labelFont', () => { it('sets the font style for the label', () => { - const result = fn(null, { - labelFont: fontStyle, - }); + const result = fn( + null, + { + labelFont: fontStyle, + }, + {} as ExecutionContext + ); expect(result.value).toHaveProperty('labelFont', fontStyle); }); @@ -67,9 +80,13 @@ describe('metric', () => { describe('metricFormat', () => { it('sets the number format of the metric value', () => { - const result = fn(null, { - metricFormat: '0.0%', - }); + const result = fn( + null, + { + metricFormat: '0.0%', + }, + {} as ExecutionContext + ); expect(result.value).toHaveProperty('metricFormat', '0.0%'); }); diff --git a/src/plugins/expression_metric/common/expression_functions/metric_function.ts b/src/plugins/expression_metric/common/expression_functions/metric_function.ts new file mode 100644 index 0000000000000..184ef37834e01 --- /dev/null +++ b/src/plugins/expression_metric/common/expression_functions/metric_function.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { openSans } from '../../../expressions/common/fonts'; +import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../constants'; +import { ExpressionMetricFunction } from '../types'; + +export const strings = { + help: i18n.translate('expressionMetric.functions.metricHelpText', { + defaultMessage: 'Displays a number over a label.', + }), + args: { + label: i18n.translate('expressionMetric.functions.metric.args.labelHelpText', { + defaultMessage: 'The text describing the metric.', + }), + labelFont: i18n.translate('expressionMetric.functions.metric.args.labelFontHelpText', { + defaultMessage: + 'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.', + values: { + CSS, + FONT_FAMILY, + FONT_WEIGHT, + }, + }), + metricFont: i18n.translate('expressionMetric.functions.metric.args.metricFontHelpText', { + defaultMessage: + 'The {CSS} font properties for the metric. For example, {FONT_FAMILY} or {FONT_WEIGHT}.', + values: { + CSS, + FONT_FAMILY, + FONT_WEIGHT, + }, + }), + // TODO: Find a way to generate the docs URL here + metricFormat: i18n.translate('expressionMetric.functions.metric.args.metricFormatHelpText', { + defaultMessage: 'A {NUMERALJS} format string. For example, {example1} or {example2}.', + values: { + example1: '`"0.0a"`', + example2: '`"0%"`', + NUMERALJS, + }, + }), + }, +}; + +export const metricFunction: ExpressionMetricFunction = () => { + const { help, args: argHelp } = strings; + + return { + name: 'metric', + aliases: [], + type: 'render', + inputTypes: ['number', 'string', 'null'], + help, + args: { + label: { + types: ['string'], + aliases: ['_', 'text', 'description'], + help: argHelp.label, + default: '""', + }, + labelFont: { + types: ['style'], + help: argHelp.labelFont, + default: `{font size=14 family="${openSans.value}" color="#000000" align=center}`, + }, + metricFont: { + types: ['style'], + help: argHelp.metricFont, + default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`, + }, + metricFormat: { + types: ['string'], + aliases: ['format'], + help: argHelp.metricFormat, + }, + }, + fn: (input, { label, labelFont, metricFont, metricFormat }) => { + return { + type: 'render', + as: 'metric', + value: { + metric: input === null ? '?' : input, + label, + labelFont, + metricFont, + metricFormat, + }, + }; + }, + }; +}; diff --git a/src/plugins/expression_metric/common/index.ts b/src/plugins/expression_metric/common/index.ts new file mode 100755 index 0000000000000..1b7668c49def5 --- /dev/null +++ b/src/plugins/expression_metric/common/index.ts @@ -0,0 +1,11 @@ +/* + * 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 * from './constants'; +export * from './types'; +export * from './expression_functions'; diff --git a/src/plugins/expression_metric/common/types/expression_functions.ts b/src/plugins/expression_metric/common/types/expression_functions.ts new file mode 100644 index 0000000000000..69573eec26b8f --- /dev/null +++ b/src/plugins/expression_metric/common/types/expression_functions.ts @@ -0,0 +1,24 @@ +/* + * 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 { ExpressionFunctionDefinition, ExpressionValueRender, Style } from '../../../expressions'; + +export type Input = number | string | null; + +export interface Arguments { + label: string; + metricFont: Style; + metricFormat: string; + labelFont: Style; +} + +export type ExpressionMetricFunction = () => ExpressionFunctionDefinition< + 'metric', + Input, + Arguments, + ExpressionValueRender +>; diff --git a/src/plugins/expression_metric/common/types/expression_renderers.ts b/src/plugins/expression_metric/common/types/expression_renderers.ts new file mode 100644 index 0000000000000..0f8635edd7f95 --- /dev/null +++ b/src/plugins/expression_metric/common/types/expression_renderers.ts @@ -0,0 +1,27 @@ +/* + * 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 { Style } from '../../../expressions/common'; + +export interface MetricRendererConfig { + /** The text to display under the metric */ + label: string; + /** Font settings for the label */ + labelFont: Style; + /** Value of the metric to display */ + metric: string | number | null; + /** Font settings for the metric */ + metricFont: Style; + /** NumeralJS format string */ + metricFormat: string; +} + +export interface NodeDimensions { + width: number; + height: number; +} diff --git a/src/plugins/expression_metric/common/types/index.ts b/src/plugins/expression_metric/common/types/index.ts new file mode 100644 index 0000000000000..ec934e7affe88 --- /dev/null +++ b/src/plugins/expression_metric/common/types/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 * from './expression_functions'; +export * from './expression_renderers'; diff --git a/src/plugins/expression_metric/jest.config.js b/src/plugins/expression_metric/jest.config.js new file mode 100644 index 0000000000000..517409460895e --- /dev/null +++ b/src/plugins/expression_metric/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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/expression_metric'], +}; diff --git a/src/plugins/expression_metric/kibana.json b/src/plugins/expression_metric/kibana.json new file mode 100755 index 0000000000000..c83a3fcb26687 --- /dev/null +++ b/src/plugins/expression_metric/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "expressionMetric", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["expressions", "presentationUtil"], + "optionalPlugins": [] +} diff --git a/src/plugins/expression_metric/public/components/index.ts b/src/plugins/expression_metric/public/components/index.ts new file mode 100644 index 0000000000000..cb7865e5f0b8c --- /dev/null +++ b/src/plugins/expression_metric/public/components/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 * from './metric_component'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/metric.tsx b/src/plugins/expression_metric/public/components/metric_component.tsx similarity index 76% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/metric.tsx rename to src/plugins/expression_metric/public/components/metric_component.tsx index b0e2266c6a360..9955dc5fa99f8 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/metric.tsx +++ b/src/plugins/expression_metric/public/components/metric_component.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React, { FunctionComponent, CSSProperties } from 'react'; @@ -21,7 +22,7 @@ interface Props { metricFormat?: string; } -export const Metric: FunctionComponent = ({ +const Metric: FunctionComponent = ({ label, metric, labelFont, @@ -39,3 +40,6 @@ export const Metric: FunctionComponent = ({ )} ); + +// eslint-disable-next-line import/no-default-export +export { Metric as default }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/__stories__/__snapshots__/metric.stories.storyshot b/src/plugins/expression_metric/public/expression_renderers/__stories__/__snapshots__/metric.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/__stories__/__snapshots__/metric.stories.storyshot rename to src/plugins/expression_metric/public/expression_renderers/__stories__/__snapshots__/metric.stories.storyshot diff --git a/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx b/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx new file mode 100644 index 0000000000000..0e04c32f52ba2 --- /dev/null +++ b/src/plugins/expression_metric/public/expression_renderers/__stories__/metric_renderer.stories.tsx @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { CSSProperties } from 'react'; +import { storiesOf } from '@storybook/react'; +import { Style } from 'src/plugins/expressions'; +import { metricRenderer } from '../metric_renderer'; +import { Render } from '../../../../presentation_util/public/__stories__'; +import { MetricRendererConfig } from '../../../common'; + +const labelFontSpec: CSSProperties = { + fontFamily: "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif", + fontWeight: 'normal', + fontStyle: 'italic', + textDecoration: 'none', + textAlign: 'center', + fontSize: '24px', + lineHeight: '1', + color: '#000000', +}; + +const metricFontSpec: CSSProperties = { + fontFamily: + "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif", + fontWeight: 'bold', + fontStyle: 'normal', + textDecoration: 'none', + textAlign: 'center', + fontSize: '48px', + lineHeight: '1', + color: '#b83c6f', +}; + +storiesOf('renderers/Metric', module) + .add('with null metric', () => { + const config: MetricRendererConfig = { + metric: null, + metricFont: {} as Style, + labelFont: {} as Style, + label: '', + metricFormat: '', + }; + return ; + }) + .add('with number metric', () => { + const config: MetricRendererConfig = { + metric: '12345.6789', + metricFont: metricFontSpec as Style, + labelFont: {} as Style, + label: '', + metricFormat: '', + }; + return ; + }) + .add('with string metric', () => { + const config: MetricRendererConfig = { + metric: '$12.34', + metricFont: metricFontSpec as Style, + labelFont: labelFontSpec as Style, + label: '', + metricFormat: '', + }; + return ; + }) + .add('with label', () => { + const config: MetricRendererConfig = { + metric: '$12.34', + metricFont: metricFontSpec as Style, + labelFont: labelFontSpec as Style, + label: 'Average price', + metricFormat: '', + }; + return ; + }) + .add('with number metric and a specified format', () => { + const config: MetricRendererConfig = { + metric: '-0.0024', + metricFont: metricFontSpec as Style, + labelFont: labelFontSpec as Style, + label: 'Average price', + metricFormat: '0.00%', + }; + return ; + }) + .add('with formatted string metric and a specified format', () => { + const config: MetricRendererConfig = { + metric: '$10000000.00', + metricFont: metricFontSpec as Style, + labelFont: labelFontSpec as Style, + label: 'Total Revenue', + metricFormat: '$0a', + }; + return ; + }) + .add('with invalid metricFont', () => { + const config: MetricRendererConfig = { + metric: '$10000000.00', + metricFont: metricFontSpec as Style, + labelFont: labelFontSpec as Style, + label: 'Total Revenue', + metricFormat: '$0a', + }; + return ; + }); diff --git a/src/plugins/expression_metric/public/expression_renderers/index.ts b/src/plugins/expression_metric/public/expression_renderers/index.ts new file mode 100644 index 0000000000000..b77e0bb76f1fd --- /dev/null +++ b/src/plugins/expression_metric/public/expression_renderers/index.ts @@ -0,0 +1,13 @@ +/* + * 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 { metricRenderer } from './metric_renderer'; + +export const renderers = [metricRenderer]; + +export { metricRenderer }; diff --git a/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx b/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx new file mode 100644 index 0000000000000..02c910640edeb --- /dev/null +++ b/src/plugins/expression_metric/public/expression_renderers/metric_renderer.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { CSSProperties, lazy } from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { i18n } from '@kbn/i18n'; +import { withSuspense } from '../../../presentation_util/public'; +import { MetricRendererConfig } from '../../common/types'; + +const strings = { + getDisplayName: () => + i18n.translate('expressionMetric.renderer.metric.displayName', { + defaultMessage: 'Metric', + }), + getHelpDescription: () => + i18n.translate('expressionMetric.renderer.metric.helpDescription', { + defaultMessage: 'Render a number over a label', + }), +}; + +const LazyMetricComponent = lazy(() => import('../components/metric_component')); +const MetricComponent = withSuspense(LazyMetricComponent); + +export const metricRenderer = (): ExpressionRenderDefinition => ({ + name: 'metric', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: MetricRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render( + , + domNode, + () => handlers.done() + ); + }, +}); diff --git a/src/plugins/expression_metric/public/index.ts b/src/plugins/expression_metric/public/index.ts new file mode 100755 index 0000000000000..cd3eacaba04ae --- /dev/null +++ b/src/plugins/expression_metric/public/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { ExpressionMetricPlugin } from './plugin'; + +export type { ExpressionMetricPluginSetup, ExpressionMetricPluginStart } from './plugin'; + +export function plugin() { + return new ExpressionMetricPlugin(); +} + +export * from './expression_renderers'; diff --git a/src/plugins/expression_metric/public/plugin.ts b/src/plugins/expression_metric/public/plugin.ts new file mode 100755 index 0000000000000..c11a5a4cdcade --- /dev/null +++ b/src/plugins/expression_metric/public/plugin.ts @@ -0,0 +1,36 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; +import { metricFunction } from '../common/expression_functions'; +import { metricRenderer } from './expression_renderers'; + +interface SetupDeps { + expressions: ExpressionsSetup; +} + +interface StartDeps { + expression: ExpressionsStart; +} + +export type ExpressionMetricPluginSetup = void; +export type ExpressionMetricPluginStart = void; + +export class ExpressionMetricPlugin + implements + Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionMetricPluginSetup { + expressions.registerFunction(metricFunction); + expressions.registerRenderer(metricRenderer); + } + + public start(core: CoreStart): ExpressionMetricPluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_metric/server/index.ts b/src/plugins/expression_metric/server/index.ts new file mode 100755 index 0000000000000..0caafe1b69262 --- /dev/null +++ b/src/plugins/expression_metric/server/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { ExpressionMetricPlugin } from './plugin'; + +export type { ExpressionMetricPluginSetup, ExpressionMetricPluginStart } from './plugin'; + +export function plugin() { + return new ExpressionMetricPlugin(); +} diff --git a/src/plugins/expression_metric/server/plugin.ts b/src/plugins/expression_metric/server/plugin.ts new file mode 100755 index 0000000000000..81c87f5c05580 --- /dev/null +++ b/src/plugins/expression_metric/server/plugin.ts @@ -0,0 +1,34 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsServerStart, ExpressionsServerSetup } from '../../expressions/server'; +import { metricFunction } from '../common'; + +interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +interface StartDeps { + expression: ExpressionsServerStart; +} + +export type ExpressionMetricPluginSetup = void; +export type ExpressionMetricPluginStart = void; + +export class ExpressionMetricPlugin + implements + Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionMetricPluginSetup { + expressions.registerFunction(metricFunction); + } + + public start(core: CoreStart): ExpressionMetricPluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_metric/tsconfig.json b/src/plugins/expression_metric/tsconfig.json new file mode 100644 index 0000000000000..5fab51496c97e --- /dev/null +++ b/src/plugins/expression_metric/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "__fixtures__/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../presentation_util/tsconfig.json" }, + { "path": "../expressions/tsconfig.json" }, + ] +} diff --git a/src/plugins/expressions/common/executor/__snapshots__/executor.test.ts.snap b/src/plugins/expressions/common/executor/__snapshots__/executor.test.ts.snap new file mode 100644 index 0000000000000..1d4a8614b2921 --- /dev/null +++ b/src/plugins/expressions/common/executor/__snapshots__/executor.test.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Executor .inject .getAllMigrations returns list of all registered migrations 1`] = ` +Object { + "7.10.0": [Function], + "7.10.1": [Function], +} +`; diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts index 3c24a3c24e01b..d3837272fb50f 100644 --- a/src/plugins/expressions/common/executor/executor.test.ts +++ b/src/plugins/expressions/common/executor/executor.test.ts @@ -200,12 +200,21 @@ describe('Executor', () => { }); }); - describe('.migrate', () => { + describe('.getAllMigrations', () => { + test('returns list of all registered migrations', () => { + const migrations = executor.getAllMigrations(); + expect(migrations).toMatchSnapshot(); + }); + }); + + describe('.migrateToLatest', () => { test('calls migrate function for every expression function in expression', () => { - executor.migrate( - parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'), - '7.10.0' - ); + executor.migrateToLatest({ + state: parseExpression( + 'foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}' + ), + version: '7.10.0', + }); expect(migrateFn).toBeCalledTimes(5); }); }); diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 7ca5a005991bd..ec6ca0323ea5c 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -21,7 +21,13 @@ import { ExpressionAstExpression, ExpressionAstFunction } from '../ast'; import { ExpressionValueError, typeSpecs } from '../expression_types/specs'; import { getByAlias } from '../util'; import { SavedObjectReference } from '../../../../core/types'; -import { PersistableStateService, SerializableState } from '../../../kibana_utils/common'; +import { + MigrateFunctionsObject, + migrateToLatest, + PersistableStateService, + SerializableState, + VersionedState, +} from '../../../kibana_utils/common'; import { ExpressionExecutionParams } from '../service'; export interface ExpressionExecOptions { @@ -244,7 +250,28 @@ export class Executor = Record Object.keys(fn.migrations)) + .flat(1) + ); + + const migrations: MigrateFunctionsObject = {}; + uniqueVersions.forEach((version) => { + migrations[version] = (state) => ({ + ...this.migrate(state, version), + }); + }); + + return migrations; + } + + public migrateToLatest(state: VersionedState) { + return migrateToLatest(this.getAllMigrations(), state) as ExpressionAstExpression; + } + + private migrate(ast: SerializableState, version: string) { return this.walkAst(cloneDeep(ast) as ExpressionAstExpression, (fn, link) => { if (!fn.migrations[version]) return link; const updatedAst = fn.migrations[version](link) as ExpressionAstFunction; diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index 7802617f3a251..cd52c8c3239de 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -18,7 +18,11 @@ import { ExecutionContract, ExecutionResult } from '../execution'; import { AnyExpressionTypeDefinition, ExpressionValueError } from '../expression_types'; import { AnyExpressionFunctionDefinition } from '../expression_functions'; import { SavedObjectReference } from '../../../../core/types'; -import { PersistableStateService, SerializableState } from '../../../kibana_utils/common'; +import { + PersistableStateService, + SerializableState, + VersionedState, +} from '../../../kibana_utils/common'; import { Adapters } from '../../../inspector/common/adapters'; import { clog, @@ -323,13 +327,18 @@ export class ExpressionsService implements PersistableStateService { + return this.executor.getAllMigrations(); + }; + + /** + * migrates an old expression to latest version + * @param state */ - public readonly migrate = (state: SerializableState, version: string) => { - return this.executor.migrate(state, version); + public migrateToLatest = (state: VersionedState) => { + return this.executor.migrateToLatest(state); }; /** diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 91906b67615e3..67c69f76be914 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -213,6 +213,10 @@ export class Executor = Record; // @deprecated (undocumented) readonly functions: FunctionsRegistry; + // Warning: (ae-forgotten-export) The symbol "MigrateFunctionsObject" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getAllMigrations(): MigrateFunctionsObject; // (undocumented) getFunction(name: string): ExpressionFunction | undefined; // (undocumented) @@ -225,10 +229,10 @@ export class Executor = Record AnyExpressionFunctionDefinition)): void; // (undocumented) @@ -599,6 +603,7 @@ export class ExpressionsService implements PersistableStateService ExpressionsService; + getAllMigrations: () => import("../../../kibana_utils/common").MigrateFunctionsObject; // (undocumented) readonly getFunction: ExpressionsServiceStart['getFunction']; readonly getFunctions: () => ReturnType; @@ -609,7 +614,7 @@ export class ExpressionsService implements PersistableStateService ReturnType; readonly inject: (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression; - readonly migrate: (state: SerializableState, version: string) => ExpressionAstExpression; + migrateToLatest: (state: VersionedState) => ExpressionAstExpression; readonly registerFunction: (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void; // (undocumented) readonly registerRenderer: (definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)) => void; @@ -1188,6 +1193,7 @@ export type UnmappedTypeStrings = 'date' | 'filter'; // Warnings were encountered during analysis: // // src/plugins/expressions/common/ast/types.ts:29:3 - (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_functions/expression_function.ts:68:5 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts // src/plugins/expressions/common/expression_types/specs/error.ts:20:5 - (ae-forgotten-export) The symbol "ErrorLike" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index cbe134548f8e6..1783c6c72c686 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -195,6 +195,10 @@ export class Executor = Record; // @deprecated (undocumented) readonly functions: FunctionsRegistry; + // Warning: (ae-forgotten-export) The symbol "MigrateFunctionsObject" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getAllMigrations(): MigrateFunctionsObject; // (undocumented) getFunction(name: string): ExpressionFunction | undefined; // (undocumented) @@ -207,10 +211,10 @@ export class Executor = Record AnyExpressionFunctionDefinition)): void; // (undocumented) @@ -943,6 +947,7 @@ export type UnmappedTypeStrings = 'date' | 'filter'; // Warnings were encountered during analysis: // // src/plugins/expressions/common/ast/types.ts:29:3 - (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_functions/expression_function.ts:68:5 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts // src/plugins/expressions/common/expression_types/specs/error.ts:20:5 - (ae-forgotten-export) The symbol "ErrorLike" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json index 1e44b43ab3639..a3a7e61e9806e 100644 --- a/src/plugins/index_pattern_field_editor/kibana.json +++ b/src/plugins/index_pattern_field_editor/kibana.json @@ -5,5 +5,10 @@ "ui": true, "requiredPlugins": ["data"], "optionalPlugins": ["usageCollection"], - "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection"] + "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection"], + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "Reusable index pattern field editor across Kibana" } diff --git a/src/plugins/index_pattern_management/kibana.json b/src/plugins/index_pattern_management/kibana.json index 60e382fb395f7..7d9d885bbf9d6 100644 --- a/src/plugins/index_pattern_management/kibana.json +++ b/src/plugins/index_pattern_management/kibana.json @@ -4,5 +4,10 @@ "server": true, "ui": true, "requiredPlugins": ["management", "data", "urlForwarding", "indexPatternFieldEditor"], - "requiredBundles": ["kibanaReact", "kibanaUtils"] + "requiredBundles": ["kibanaReact", "kibanaUtils"], + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "Index pattern management app" } diff --git a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts index 2ae376e787d2f..32fb652d41632 100644 --- a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts +++ b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.test.ts @@ -50,14 +50,11 @@ test('returns the same object if there are no migrations to be applied', () => { } ); - expect(migrated).toEqual({ - state: { name: 'Foo' }, - version: '0.0.1', - }); + expect(migrated).toEqual({ name: 'Foo' }); }); test('applies a single migration', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '0.0.2': (migrationV2 as unknown) as MigrateFunction, }, @@ -71,11 +68,10 @@ test('applies a single migration', () => { firstName: 'Foo', lastName: '', }); - expect(newVersion).toEqual('0.0.2'); }); test('does not apply migration if it has the same version as state', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '0.0.54': (migrationV2 as unknown) as MigrateFunction, }, @@ -88,11 +84,10 @@ test('does not apply migration if it has the same version as state', () => { expect(newState).toEqual({ name: 'Foo', }); - expect(newVersion).toEqual('0.0.54'); }); test('does not apply migration if it has lower version', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '0.2.2': (migrationV2 as unknown) as MigrateFunction, }, @@ -105,11 +100,10 @@ test('does not apply migration if it has lower version', () => { expect(newState).toEqual({ name: 'Foo', }); - expect(newVersion).toEqual('0.3.1'); }); test('applies two migrations consecutively', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '7.14.0': (migrationV2 as unknown) as MigrateFunction, '7.14.2': (migrationV3 as unknown) as MigrateFunction, @@ -126,11 +120,10 @@ test('applies two migrations consecutively', () => { isAdmin: false, age: 0, }); - expect(newVersion).toEqual('7.14.2'); }); test('applies only migrations which are have higher semver version', () => { - const { state: newState, version: newVersion } = migrateToLatest( + const newState = migrateToLatest( { '7.14.0': (migrationV2 as unknown) as MigrateFunction, // not applied '7.14.1': (() => ({})) as MigrateFunction, // not applied @@ -148,5 +141,4 @@ test('applies only migrations which are have higher semver version', () => { isAdmin: false, age: 0, }); - expect(newVersion).toEqual('7.14.2'); }); diff --git a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts index c16392164e3e4..6f81d0a7b9b63 100644 --- a/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts +++ b/src/plugins/kibana_utils/common/persistable_state/migrate_to_latest.ts @@ -12,19 +12,16 @@ import { SerializableState, VersionedState, MigrateFunctionsObject } from './typ export function migrateToLatest( migrations: MigrateFunctionsObject, { state, version: oldVersion }: VersionedState -): VersionedState { +): S { const versions = Object.keys(migrations || {}) .filter((v) => compare(v, oldVersion) > 0) .sort(compare); - if (!versions.length) return { state, version: oldVersion } as VersionedState; + if (!versions.length) return state as S; for (const version of versions) { state = migrations[version]!(state); } - return { - state: state as S, - version: versions[versions.length - 1], - }; + return state as S; } diff --git a/src/plugins/kibana_utils/common/persistable_state/types.ts b/src/plugins/kibana_utils/common/persistable_state/types.ts index 8d808dcfec2a4..a2d1751297a9f 100644 --- a/src/plugins/kibana_utils/common/persistable_state/types.ts +++ b/src/plugins/kibana_utils/common/persistable_state/types.ts @@ -174,5 +174,5 @@ export interface PersistableStateService

MigrateFunctionsObject; + getAllMigrations: () => MigrateFunctionsObject; } diff --git a/src/plugins/presentation_util/common/lib/test_helpers/index.ts b/src/plugins/presentation_util/common/lib/test_helpers/index.ts index a6ea8da6ac6e9..486df287bf16b 100644 --- a/src/plugins/presentation_util/common/lib/test_helpers/index.ts +++ b/src/plugins/presentation_util/common/lib/test_helpers/index.ts @@ -7,3 +7,4 @@ */ export * from './function_wrapper'; +export * from './test_styles'; diff --git a/src/plugins/presentation_util/common/lib/test_helpers/test_styles.ts b/src/plugins/presentation_util/common/lib/test_helpers/test_styles.ts new file mode 100644 index 0000000000000..99b7ce6c38d2b --- /dev/null +++ b/src/plugins/presentation_util/common/lib/test_helpers/test_styles.ts @@ -0,0 +1,23 @@ +/* + * 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 fontStyle = { + type: 'style', + spec: { + fontFamily: 'Chalkboard, serif', + fontWeight: 'bolder', + fontStyle: 'normal', + textDecoration: 'underline', + color: 'pink', + textAlign: 'center', + fontSize: '14px', + lineHeight: '21px', + }, + css: + 'font-family:Chalkboard, serif;font-weight:bolder;font-style:normal;text-decoration:underline;color:pink;text-align:center;font-size:14px;line-height:21px', +}; diff --git a/src/plugins/presentation_util/public/services/create/factory.ts b/src/plugins/presentation_util/public/services/create/factory.ts index 4e2d1dd291073..ddc2e5845b037 100644 --- a/src/plugins/presentation_util/public/services/create/factory.ts +++ b/src/plugins/presentation_util/public/services/create/factory.ts @@ -7,7 +7,7 @@ */ import { BehaviorSubject } from 'rxjs'; -import { CoreStart, AppUpdater } from 'src/core/public'; +import { CoreStart, AppUpdater, PluginInitializerContext } from 'src/core/public'; /** * A factory function for creating a service. @@ -28,6 +28,7 @@ export interface KibanaPluginServiceParams { coreStart: CoreStart; startPlugins: Start; appUpdater?: BehaviorSubject; + initContext?: PluginInitializerContext; } /** diff --git a/src/plugins/share/public/url_service/redirect/redirect_manager.ts b/src/plugins/share/public/url_service/redirect/redirect_manager.ts index 6148249f5a047..ad99be43f678a 100644 --- a/src/plugins/share/public/url_service/redirect/redirect_manager.ts +++ b/src/plugins/share/public/url_service/redirect/redirect_manager.ts @@ -68,7 +68,7 @@ export class RedirectManager { throw error; } - const { state: migratedParams } = migrateToLatest(locator.migrations, { + const migratedParams = migrateToLatest(locator.migrations, { state: options.params, version: options.version, }); diff --git a/src/plugins/usage_collection/public/components/track_application_view/track_application_view.test.tsx b/src/plugins/usage_collection/public/components/track_application_view/track_application_view.test.tsx index 23072dd087551..1cb32280d52f6 100644 --- a/src/plugins/usage_collection/public/components/track_application_view/track_application_view.test.tsx +++ b/src/plugins/usage_collection/public/components/track_application_view/track_application_view.test.tsx @@ -7,22 +7,21 @@ */ import React from 'react'; -import { mountWithIntl } from '@kbn/test/jest'; import { ApplicationUsageContext, TrackApplicationView } from './track_application_view'; import { IApplicationUsageTracker } from '../../plugin'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, render } from '@testing-library/react'; describe('TrackApplicationView', () => { test('it renders the internal component even when no tracker has been set', () => { - const component = mountWithIntl( + const { unmount } = render(

Hello

); - component.unmount(); + unmount(); }); - test('it tracks the component while it is rendered', () => { + test('it tracks the component while it is rendered', async () => { const applicationUsageTrackerMock: jest.Mocked = { trackApplicationViewUsage: jest.fn(), flushTrackedView: jest.fn(), @@ -30,7 +29,7 @@ describe('TrackApplicationView', () => { }; expect(applicationUsageTrackerMock.trackApplicationViewUsage).not.toHaveBeenCalled(); const viewId = 'testView'; - const component = mountWithIntl( + const { findByText, unmount } = render(

Hello

@@ -39,10 +38,11 @@ describe('TrackApplicationView', () => { ); expect(applicationUsageTrackerMock.trackApplicationViewUsage).toHaveBeenCalledWith(viewId); expect(applicationUsageTrackerMock.updateViewClickCounter).not.toHaveBeenCalled(); - fireEvent.click(component.getDOMNode()); + const element = await findByText('Hello'); + fireEvent.click(element); expect(applicationUsageTrackerMock.updateViewClickCounter).toHaveBeenCalledWith(viewId); expect(applicationUsageTrackerMock.flushTrackedView).not.toHaveBeenCalled(); - component.unmount(); + unmount(); expect(applicationUsageTrackerMock.flushTrackedView).toHaveBeenCalledWith(viewId); }); }); diff --git a/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.test.tsx b/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.test.tsx index c4e2752d3349b..3131f2d527637 100644 --- a/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.test.tsx +++ b/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.test.tsx @@ -7,22 +7,21 @@ */ import React from 'react'; -import { mountWithIntl } from '@kbn/test/jest'; import { TrackApplicationViewComponent } from './track_application_view_component'; import { IApplicationUsageTracker } from '../../plugin'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, render } from '@testing-library/react'; describe('TrackApplicationViewComponent', () => { test('it renders the internal component even when no tracker is provided', () => { - const component = mountWithIntl( + const { unmount } = render(

Hello

); - component.unmount(); + unmount(); }); - test('it tracks the component while it is rendered', () => { + test('it tracks the component while it is rendered', async () => { const applicationUsageTrackerMock: jest.Mocked = { trackApplicationViewUsage: jest.fn(), flushTrackedView: jest.fn(), @@ -30,7 +29,7 @@ describe('TrackApplicationViewComponent', () => { }; expect(applicationUsageTrackerMock.trackApplicationViewUsage).not.toHaveBeenCalled(); const viewId = 'testView'; - const component = mountWithIntl( + const { findByText, unmount } = render( { ); expect(applicationUsageTrackerMock.trackApplicationViewUsage).toHaveBeenCalledWith(viewId); expect(applicationUsageTrackerMock.updateViewClickCounter).not.toHaveBeenCalled(); - fireEvent.click(component.getDOMNode()); + const element = await findByText('Hello'); + fireEvent.click(element); expect(applicationUsageTrackerMock.updateViewClickCounter).toHaveBeenCalledWith(viewId); expect(applicationUsageTrackerMock.flushTrackedView).not.toHaveBeenCalled(); - component.unmount(); + unmount(); expect(applicationUsageTrackerMock.flushTrackedView).toHaveBeenCalledWith(viewId); }); }); diff --git a/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.tsx b/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.tsx index f18b37df484fc..a1feb93b55db2 100644 --- a/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.tsx +++ b/src/plugins/usage_collection/public/components/track_application_view/track_application_view_component.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Component } from 'react'; +import React from 'react'; import ReactDOM from 'react-dom'; import { IApplicationUsageTracker } from '../../plugin'; import { TrackApplicationViewProps } from './types'; @@ -15,17 +15,22 @@ interface Props extends TrackApplicationViewProps { applicationUsageTracker?: IApplicationUsageTracker; } -export class TrackApplicationViewComponent extends Component { - onClick = () => { +export class TrackApplicationViewComponent extends React.Component { + private parentNode: (Node & ParentNode) | null | undefined; + + onClick = (e: MouseEvent) => { const { applicationUsageTracker, viewId } = this.props; - applicationUsageTracker?.updateViewClickCounter(viewId); + this.parentNode = this.parentNode || ReactDOM.findDOMNode(this)?.parentNode; + if (this.parentNode === e.target || this.parentNode?.contains(e.target as Node | null)) { + applicationUsageTracker?.updateViewClickCounter(viewId); + } }; componentDidMount() { const { applicationUsageTracker, viewId } = this.props; if (applicationUsageTracker) { applicationUsageTracker.trackApplicationViewUsage(viewId); - ReactDOM.findDOMNode(this)?.parentNode?.addEventListener('click', this.onClick); + document.addEventListener('click', this.onClick); } } @@ -33,8 +38,8 @@ export class TrackApplicationViewComponent extends Component { const { applicationUsageTracker, viewId } = this.props; if (applicationUsageTracker) { applicationUsageTracker.flushTrackedView(viewId); - ReactDOM.findDOMNode(this)?.parentNode?.removeEventListener('click', this.onClick); } + document.removeEventListener('click', this.onClick); } render() { diff --git a/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx b/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx index 9f1d5e0db4583..19164d0aefe52 100644 --- a/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx +++ b/src/plugins/vis_type_pie/public/utils/get_legend_actions.tsx @@ -81,6 +81,9 @@ export const getLegendActions = ( const Button = (
undefined} + onKeyPress={() => setPopoverOpen(!popoverOpen)} onClick={() => setPopoverOpen(!popoverOpen)} > diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx index 1dfb96b419d49..cc737561bedac 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx @@ -176,6 +176,7 @@ export const AnnotationRow = ({ }} onChange={handleQueryChange} indexPatterns={[model.index_pattern]} + data-test-subj="annotationQueryBar" /> @@ -240,7 +241,12 @@ export const AnnotationRow = ({ } fullWidth > - + @@ -267,6 +273,7 @@ export const AnnotationRow = ({ onChange={handleChange('template')} value={model.template} fullWidth + data-test-subj="annotationRowTemplateInput" /> diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx index b3b4993d2ca06..b3683602aa345 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx @@ -41,7 +41,7 @@ const NoContent = ({ handleAdd }: { handleAdd: () => void }) => ( defaultMessage="Click the button below to create an annotation data source." />

- + @@ -442,6 +443,7 @@ export class TimeseriesPanelConfig extends Component< this.switchTab(PANEL_CONFIG_TABS.ANNOTATIONS)} + data-test-subj="timeSeriesEditorAnnotationsBtn" > onChange({ filter })} indexPatterns={[indexPatternForQuery]} + data-test-subj="seriesConfigQueryBar" /> diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js b/src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js index 2b4324f95adad..739988b0c2107 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js +++ b/src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js @@ -73,6 +73,7 @@ class FilterItemsUi extends Component { }} onChange={(query) => this.handleQueryChange(model, query)} indexPatterns={[indexPatterns]} + data-test-subj="filterItemsQueryBar" /> @@ -88,6 +89,7 @@ class FilterItemsUi extends Component { onChange={this.handleChange(model, 'label')} value={model.label} fullWidth + data-test-subj="filterItemsLabel" /> @@ -96,6 +98,7 @@ class FilterItemsUi extends Component { onDelete={handleDelete} disableDelete={items.length < 2} responsive={false} + testSubj="filterRow" /> diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js b/src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js index 7381e66adcd0c..68367b0ea55eb 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js +++ b/src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js @@ -61,6 +61,7 @@ function GroupBySelectUi(props) { selectedOptions={[selectedOption]} onChange={props.onChange} singleSelection={{ asPlainText: true }} + data-test-subj="groupBySelect" /> ); } diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js index 6f4b0c4cf56cb..81b8626261f43 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js @@ -159,6 +159,7 @@ export const TimeseriesConfig = injectI18n(function (props) { selectedOptions={selectedChartTypeOption ? [selectedChartTypeOption] : []} onChange={handleSelectChange('chart_type')} singleSelection={{ asPlainText: true }} + data-test-subj="seriesChartTypeComboBox" /> @@ -261,6 +262,7 @@ export const TimeseriesConfig = injectI18n(function (props) { selectedOptions={selectedChartTypeOption ? [selectedChartTypeOption] : []} onChange={handleSelectChange('chart_type')} singleSelection={{ asPlainText: true }} + data-test-subj="seriesChartTypeComboBox" /> @@ -530,6 +532,7 @@ export const TimeseriesConfig = injectI18n(function (props) { value={model.override_index_pattern} name="override_index_pattern" onChange={props.onChange} + data-test-subj="seriesOverrideIndexPattern" /> diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js index ed62c0909e51b..76bf3ca9d1b6e 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js @@ -143,6 +143,7 @@ export const TimeSeries = ({ return ( undefined} + onKeyPress={() => setPopoverOpen(!popoverOpen)} onClick={() => setPopoverOpen(!popoverOpen)} > diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index cc57d58348180..df6a879f5a0a9 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -185,6 +185,253 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(hasMachineRawFilter).to.be(true); }); }); + + describe('Elastic charts', () => { + beforeEach(async () => { + await visualBuilder.toggleNewChartsLibraryWithDebug(true); + await visualBuilder.clickPanelOptions('timeSeries'); + await visualBuilder.setIntervalValue('12h'); + await visualBuilder.clickDataTab('timeSeries'); + }); + + it('should display correct chart data for average aggregation', async () => { + const expectedChartData = [ + [1442707200000, 5765.324917218543], + [1442750400000, 5635.074754378471], + [1442793600000, 5798.3942307692305], + [1442836800000, 5721.522355975924], + [1442880000000, 5639.770887166236], + ]; + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes'); + + const chartData = await visualBuilder.getAreaChartData(); + expect(chartData).to.eql(expectedChartData); + }); + + it('should display correct chart data for percentile aggregation', async () => { + const expectedChartData = [ + [1442707200000, 157580], + [1442750400000, 226400], + [1442793600000, 200920], + [1442836800000, 202320], + [1442880000000, 171720], + ]; + await visualBuilder.selectAggType('Percentile'); + await visualBuilder.setFieldForAggregation('Memory'); + + const chartData = await visualBuilder.getAreaChartData(); + expect(chartData).to.eql(expectedChartData); + }); + + it('should display correct chart data, label names and area colors for sum aggregation when split by terms', async () => { + const firstAreaExpectedChartData = [ + [1442620800000, 0], + [1442664000000, 0], + [1442707200000, 11121455], + [1442750400000, 10611145], + [1442793600000, 10511084], + [1442836800000, 10512452], + [1442880000000, 10444101], + ]; + const secondAreaExpectedChartData = [ + [1442620800000, 0], + [1442664000000, 0], + [1442707200000, 2807570], + [1442750400000, 2580565], + [1442793600000, 2755642], + [1442836800000, 2795809], + [1442880000000, 2651447], + ]; + await visualBuilder.selectAggType('Sum'); + await visualBuilder.setFieldForAggregation('bytes'); + await visualBuilder.setMetricsGroupByTerms('type'); + + const chartDebugData = await visualBuilder.getChartDebugState(); + const areasCount = (await visualBuilder.getChartItems(chartDebugData))?.length; + const legendNames = await visualBuilder.getLegendNames(chartDebugData); + const areaColors = await visualBuilder.getAreaChartColors(chartDebugData); + const firstAreaChartData = await visualBuilder.getAreaChartData(chartDebugData); + const secondAreaChartData = await visualBuilder.getAreaChartData(chartDebugData, 1); + + expect(areasCount).to.be(2); + expect(legendNames).to.eql(['apache', 'nginx']); + expect(areaColors).to.eql(['#54b399', '#6092c0']); + expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); + expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); + }); + + it('should display correct chart data, label names and area colors for min aggregation when split by filters', async () => { + const firstAreaExpectedChartData = [ + [1442707200000, 219120], + [1442750400000, 209840], + [1442793600000, 200920], + [1442836800000, 202320], + [1442880000000, 201080], + ]; + const secondAreaExpectedChartData = [ + [1442707200000, 293120], + [1442750400000, 289960], + [1442793600000, 297800], + [1442836800000, 281040], + [1442880000000, 282080], + ]; + await visualBuilder.selectAggType('Min'); + await visualBuilder.setFieldForAggregation('memory'); + await visualBuilder.setMetricsGroupBy('filters'); + await visualBuilder.addGroupByFilterRow(); + await visualBuilder.setGroupByFilterQuery('bytes > 5000'); + await visualBuilder.setGroupByFilterQuery('bytes > 7000', 1); + await visualBuilder.setGroupByFilterLabel('second', 1); + await visualBuilder.setColorPickerValue('#00BCA3', 1); + await visualBuilder.setColorPickerValue('#72CFC2', 2); + + const chartDebugData = await visualBuilder.getChartDebugState(); + const areasCount = (await visualBuilder.getChartItems(chartDebugData))?.length; + const legendNames = await visualBuilder.getLegendNames(chartDebugData); + const areaColors = await visualBuilder.getAreaChartColors(chartDebugData); + const firstAreaChartData = await visualBuilder.getAreaChartData(chartDebugData); + const secondAreaChartData = await visualBuilder.getAreaChartData(chartDebugData, 1); + + expect(areasCount).to.be(2); + expect(legendNames).to.eql(['bytes > 5000', 'second']); + expect(areaColors).to.eql(['rgba(0,188,163,1)', 'rgba(114,207,194,1)']); + expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); + expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); + }); + + it('should display cloned series and then change its chart type to bar', async () => { + let areasCount = (await visualBuilder.getChartItems())?.length; + expect(areasCount).to.be(1); + + await visualBuilder.cloneSeries(); + areasCount = (await visualBuilder.getChartItems())?.length; + expect(areasCount).to.be(2); + + await visualBuilder.clickSeriesOption(); + await visualBuilder.setChartType('Bar'); + + const chartDebugData = await visualBuilder.getChartDebugState(); + areasCount = (await visualBuilder.getChartItems(chartDebugData))?.length; + const barsCount = (await visualBuilder.getChartItems(chartDebugData))?.length; + expect(areasCount).to.be(1); + expect(barsCount).to.be(1); + }); + + it('should display correct chart data for overridden index pattern', async () => { + const expectedChartData = [ + [1442620800000, 4], + [1442664000000, 3], + [1442707200000, 5], + [1442750400000, 2], + [1442793600000, 6], + [1442836800000, 1], + [1442880000000, 6], + [1442923200000, 1], + ]; + await visualBuilder.clickSeriesOption(); + await visualBuilder.setOverrideIndexPattern(true); + await visualBuilder.setIndexPatternValue('long-window-logstash-*'); + await visualBuilder.setIntervalValue('12h'); + + const chartData = await visualBuilder.getAreaChartData(); + expect(chartData).to.eql(expectedChartData); + }); + + it('should display correct data for the selected interval', async () => { + const expectedChartData = [ + [1442534400000, 0], + [1442707200000, 9371], + ]; + await visualBuilder.clickPanelOptions('timeSeries'); + await visualBuilder.setIntervalValue('2d'); + + const chartDebugData = await visualBuilder.getChartDebugState(); + const title = await visualBuilder.getXAxisTitle(chartDebugData); + const chartData = await visualBuilder.getAreaChartData(chartDebugData); + + expect(title).to.be('per 2 days'); + expect(chartData).to.eql(expectedChartData); + }); + + describe('Query filter', () => { + it('should display correct chart data for applied series filter', async () => { + const expectedChartData = [ + [1442620800000, 0], + [1442664000000, 0], + [1442707200000, 31], + [1442750400000, 24], + [1442793600000, 27], + [1442836800000, 22], + [1442880000000, 24], + ]; + await visualBuilder.clickSeriesOption(); + await visualBuilder.setSeriesFilter('machine.os.raw : "win 7" and bytes > 10000'); + + const chartData = await visualBuilder.getAreaChartData(); + expect(chartData).to.eql(expectedChartData); + }); + + it('should display correct chart data for applied panel filter', async () => { + const expectedChartData = [ + [1442620800000, 0], + [1442664000000, 0], + [1442707200000, 472], + [1442750400000, 474], + [1442793600000, 450], + [1442836800000, 439], + [1442880000000, 458], + ]; + await visualBuilder.clickPanelOptions('timeSeries'); + await visualBuilder.setPanelFilter('machine.os.raw: "ios"'); + + const chartData = await visualBuilder.getAreaChartData(); + expect(chartData).to.eql(expectedChartData); + }); + }); + + describe('Annotations', () => { + it('should display correct annotations amount with tooltip data for the selected annotation', async () => { + await visualBuilder.clickAnnotationsTab(); + await visualBuilder.clickAnnotationsAddDataSourceButton(); + await visualBuilder.setAnnotationFilter('bytes = 0'); + await visualBuilder.setAnnotationFields('machine.os.raw, memory'); + await visualBuilder.setAnnotationRowTemplate( + 'OS: {{machine.os.raw}}, memory: {{memory}}' + ); + + const annotationsCount = await visualBuilder.getAnnotationsCount(); + await visualBuilder.clickAnnotationIcon(3); + const annotationTooltipHeader = await visualBuilder.getAnnotationTooltipHeader(); + const annotationTooltipDetails = await visualBuilder.getAnnotationTooltipDetails(); + + expect(annotationsCount).to.be(6); + expect(annotationTooltipHeader).to.be('2015-09-21 06:00'); + expect(annotationTooltipDetails).to.be('OS: ios, memory: 0'); + }); + + it('should display correct annotations amount with tooltip data for the selected annotation when using runtime field', async () => { + await visualBuilder.clickAnnotationsTab(); + await visualBuilder.clickAnnotationsAddDataSourceButton(); + await visualBuilder.setAnnotationFilter('memory > 300000'); + await visualBuilder.setAnnotationFields('hello_world_runtime_field, geo.dest'); + await visualBuilder.setAnnotationRowTemplate( + '{{hello_world_runtime_field}} from {{geo.dest}}!' + ); + + const annotationsCount = await visualBuilder.getAnnotationsCount(); + await visualBuilder.clickAnnotationIcon(5); + const annotationTooltipHeader = await visualBuilder.getAnnotationTooltipHeader(); + const annotationTooltipDetails = await visualBuilder.getAnnotationTooltipDetails(); + + expect(annotationsCount).to.be(30); + expect(annotationTooltipHeader).to.be('2015-09-20 11:00'); + expect(annotationTooltipDetails).to.be('hello world from GA!'); + }); + }); + + after(async () => await visualBuilder.toggleNewChartsLibraryWithDebug(false)); + }); }); }); } diff --git a/test/functional/config.js b/test/functional/config.js index 1c0c519f21e4c..19a628be10f52 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -106,6 +106,12 @@ export default async function ({ readConfigFile }) { observabilityCases: { pathname: '/app/observability/cases', }, + fleet: { + pathname: '/app/fleet', + }, + integrations: { + pathname: '/app/integrations', + }, }, junit: { reportName: 'Chrome UI Functional Tests', diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 40a70efd93efd..ea9e23da8256f 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { DebugState } from '@elastic/charts'; import { FtrService } from '../ftr_provider_context'; import { WebElementWrapper } from '../services/lib/web_element_wrapper'; @@ -28,6 +29,8 @@ export class VisualBuilderPageObject extends FtrService { private readonly retry = this.ctx.getService('retry'); private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly comboBox = this.ctx.getService('comboBox'); + private readonly elasticChart = this.ctx.getService('elasticChart'); + private readonly kibanaServer = this.ctx.getService('kibanaServer'); private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); private readonly timePicker = this.ctx.getPageObject('timePicker'); @@ -454,6 +457,51 @@ export class VisualBuilderPageObject extends FtrService { await this.header.waitUntilLoadingHasFinished(); } + public async clickAnnotationsTab() { + await this.testSubjects.click('timeSeriesEditorAnnotationsBtn'); + await this.header.waitUntilLoadingHasFinished(); + } + + public async clickAnnotationsAddDataSourceButton() { + await this.testSubjects.click('addDataSourceButton'); + } + + public async setAnnotationFilter(query: string) { + const annotationQueryBar = await this.testSubjects.find('annotationQueryBar'); + await annotationQueryBar.type(query); + await this.header.waitUntilLoadingHasFinished(); + } + + public async setAnnotationFields(fields: string) { + const annotationFieldsInput = await this.testSubjects.find('annotationFieldsInput'); + await annotationFieldsInput.type(fields); + } + + public async setAnnotationRowTemplate(template: string) { + const annotationRowTemplateInput = await this.testSubjects.find('annotationRowTemplateInput'); + await annotationRowTemplateInput.type(template); + } + + public async getAnnotationsCount() { + const annotationsIcons = (await this.find.allByCssSelector('.echAnnotation')) ?? []; + return annotationsIcons.length; + } + + public async clickAnnotationIcon(nth: number = 0) { + const annotationsIcons = (await this.find.allByCssSelector('.echAnnotation')) ?? []; + await annotationsIcons[nth].click(); + } + + public async getAnnotationTooltipHeader() { + const annotationTooltipHeader = await this.find.byClassName('echAnnotation__header'); + return await annotationTooltipHeader.getVisibleText(); + } + + public async getAnnotationTooltipDetails() { + const annotationTooltipDetails = await this.find.byClassName('echAnnotation__details'); + return await annotationTooltipDetails.getVisibleText(); + } + public async switchIndexPatternSelectionMode(useKibanaIndices: boolean) { await this.testSubjects.click('switchIndexPatternSelectionModePopover'); await this.testSubjects.setEuiSwitch( @@ -489,7 +537,7 @@ export class VisualBuilderPageObject extends FtrService { public async setIntervalValue(value: string) { const el = await this.testSubjects.find('metricsIndexPatternInterval'); - await el.clearValue(); + await el.clearValueWithKeyboard(); await el.type(value); await this.header.waitUntilLoadingHasFinished(); } @@ -500,6 +548,14 @@ export class VisualBuilderPageObject extends FtrService { await this.header.waitUntilLoadingHasFinished(); } + public async setOverrideIndexPattern(value: boolean) { + const option = await this.testSubjects.find( + `seriesOverrideIndexPattern-${value ? 'yes' : 'no'}` + ); + (await option.findByCssSelector('label')).click(); + await this.header.waitUntilLoadingHasFinished(); + } + public async waitForIndexPatternTimeFieldOptionsLoaded() { await this.retry.waitFor('combobox options loaded', async () => { const options = await this.comboBox.getOptions('metricsIndexPatternFieldsSelect'); @@ -689,14 +745,16 @@ export class VisualBuilderPageObject extends FtrService { return await this.find.allByCssSelector('.tvbSeriesEditor'); } + public async setMetricsGroupBy(option: string) { + const groupBy = await this.testSubjects.find('groupBySelect'); + await this.comboBox.setElement(groupBy, option, { clickWithMouse: true }); + } + public async setMetricsGroupByTerms( field: string, filtering: { include?: string; exclude?: string } = {} ) { - const groupBy = await this.find.byCssSelector( - '.tvbAggRow--split [data-test-subj="comboBoxInput"]' - ); - await this.comboBox.setElement(groupBy, 'Terms', { clickWithMouse: true }); + await this.setMetricsGroupBy('terms'); await this.common.sleep(1000); const byField = await this.testSubjects.find('groupByField'); await this.comboBox.setElement(byField, field); @@ -725,6 +783,38 @@ export class VisualBuilderPageObject extends FtrService { return await this.comboBox.isOptionSelected(groupBy, value); } + public async addGroupByFilterRow() { + const addButton = await this.testSubjects.find('filterRowAddBtn'); + await addButton.click(); + } + + public async setGroupByFilterQuery(query: string, nth: number = 0) { + const filterQueryInput = await this.testSubjects.findAll('filterItemsQueryBar'); + await filterQueryInput[nth].type(query); + } + + public async setGroupByFilterLabel(label: string, nth: number = 0) { + const filterLabelInput = await this.testSubjects.findAll('filterItemsLabel'); + await filterLabelInput[nth].type(label); + } + + public async setChartType(type: string, nth: number = 0) { + const seriesChartTypeComboBoxes = await this.testSubjects.findAll('seriesChartTypeComboBox'); + return await this.comboBox.setElement(seriesChartTypeComboBoxes[nth], type); + } + + public async setSeriesFilter(query: string) { + const seriesFilterQueryInput = await this.testSubjects.find('seriesConfigQueryBar'); + await seriesFilterQueryInput.type(query); + await this.header.waitUntilLoadingHasFinished(); + } + + public async setPanelFilter(query: string) { + const panelFilterQueryInput = await this.testSubjects.find('panelFilterQueryBar'); + await panelFilterQueryInput.type(query); + await this.header.waitUntilLoadingHasFinished(); + } + public async setMetricsDataTimerangeMode(value: string) { const dataTimeRangeMode = await this.testSubjects.find('dataTimeRangeMode'); return await this.comboBox.setElement(dataTimeRangeMode, value); @@ -750,4 +840,39 @@ export class VisualBuilderPageObject extends FtrService { const optionInput = await this.testSubjects.find(`filterRatio${optionType}Input`); await optionInput.type(query); } + + public async toggleNewChartsLibraryWithDebug(enabled: boolean) { + await this.kibanaServer.uiSettings.update({ + 'visualization:visualize:legacyChartsLibrary': !enabled, + }); + await this.elasticChart.setNewChartUiDebugFlag(enabled); + } + + public async getChartDebugState(chartData?: DebugState) { + return chartData ?? (await this.elasticChart.getChartDebugData())!; + } + + public async getXAxisTitle(chartData?: DebugState, nth: number = 0) { + const debugState = await this.getChartDebugState(chartData); + return debugState?.axes?.x[nth]?.title; + } + + public async getLegendNames(chartData?: DebugState) { + const legendItems = (await this.getChartDebugState(chartData))?.legend?.items ?? []; + return legendItems.map(({ name }) => name); + } + + public async getChartItems(chartData?: DebugState, itemType: 'areas' | 'bars' = 'areas') { + return (await this.getChartDebugState(chartData))?.[itemType]; + } + + public async getAreaChartColors(chartData?: DebugState) { + const areas = (await this.getChartItems(chartData)) as DebugState['areas']; + return areas?.map(({ color }) => color); + } + + public async getAreaChartData(chartData?: DebugState, nth: number = 0) { + const areas = (await this.getChartItems(chartData)) as DebugState['areas']; + return areas?.[nth]?.lines.y1.points.map(({ x, y }) => [x, y]); + } } diff --git a/test/package/deb.yml b/test/package/deb.yml index 294657e99473f..9133402d3eee3 100644 --- a/test/package/deb.yml +++ b/test/package/deb.yml @@ -6,6 +6,7 @@ - assert_keystore_cli - assert_kibana_yml - assert_kibana_listening - - assert_kibana_available + # flaky https://github.com/elastic/kibana/issues/106749 + # - assert_kibana_available - assert_kibana_log - assert_kibana_data diff --git a/test/package/docker.yml b/test/package/docker.yml index 6da20360c174a..141f18e2c222a 100644 --- a/test/package/docker.yml +++ b/test/package/docker.yml @@ -4,4 +4,5 @@ - install_docker - install_kibana_docker - assert_kibana_listening - - assert_kibana_available + # flaky https://github.com/elastic/kibana/issues/106749 + # - assert_kibana_available \ No newline at end of file diff --git a/test/package/rpm.yml b/test/package/rpm.yml index 456c2bdf18b72..5fb1d37a96918 100644 --- a/test/package/rpm.yml +++ b/test/package/rpm.yml @@ -6,6 +6,7 @@ - assert_keystore_cli - assert_kibana_yml - assert_kibana_listening - - assert_kibana_available + # flaky https://github.com/elastic/kibana/issues/106749 + # - assert_kibana_available - assert_kibana_log - assert_kibana_data diff --git a/test/plugin_functional/plugins/index_patterns/kibana.json b/test/plugin_functional/plugins/index_patterns/kibana.json index 3b41fa5124a45..290240ff6a768 100644 --- a/test/plugin_functional/plugins/index_patterns/kibana.json +++ b/test/plugin_functional/plugins/index_patterns/kibana.json @@ -5,5 +5,10 @@ "configPath": ["index_patterns_test_plugin"], "server": true, "ui": false, - "requiredPlugins": ["data"] + "requiredPlugins": ["data"], + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "Index pattern test plugin" } diff --git a/test/plugin_functional/test_suites/core/deprecations.ts b/test/plugin_functional/test_suites/core/deprecations.ts index 112b29a896a00..2bdd3cf59549b 100644 --- a/test/plugin_functional/test_suites/core/deprecations.ts +++ b/test/plugin_functional/test_suites/core/deprecations.ts @@ -29,6 +29,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }, deprecationType: 'config', domainId: 'corePluginDeprecations', + requireRestart: true, }, { level: 'critical', @@ -40,6 +41,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }, deprecationType: 'config', domainId: 'corePluginDeprecations', + requireRestart: true, }, { level: 'critical', @@ -53,6 +55,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide documentationUrl: 'config-secret-doc-url', deprecationType: 'config', domainId: 'corePluginDeprecations', + requireRestart: true, }, { message: 'CorePluginDeprecationsPlugin is a deprecated feature for testing.', diff --git a/test/scripts/checks/type_check_plugin_public_api_docs.sh b/test/scripts/checks/type_check_plugin_public_api_docs.sh index d32343fcd173d..24be42d63207a 100755 --- a/test/scripts/checks/type_check_plugin_public_api_docs.sh +++ b/test/scripts/checks/type_check_plugin_public_api_docs.sh @@ -14,4 +14,4 @@ checks-reporter-with-killswitch "Check Types" \ node scripts/type_check echo " -- building api docs" -node scripts/build_api_docs +node --max-old-space-size=8000 scripts/build_api_docs diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx index a9a0b824cc828..f77a6591270c4 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx @@ -7,6 +7,7 @@ import { EuiButton, + EuiCallOut, EuiCard, EuiFlexGroup, EuiFlexItem, @@ -271,6 +272,30 @@ export function SchemaOverviewHeading() { /> + + + + + {i18n.translate( + 'xpack.apm.settings.schema.descriptionText.betaCalloutMessage', + { + defaultMessage: + 'This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.', + } + )} + + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/correlations/correlations_chart.tsx b/x-pack/plugins/apm/public/components/app/correlations/correlations_chart.tsx index cfc57d3b3e4a3..50d5f28aa5d55 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/correlations_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/correlations_chart.tsx @@ -72,7 +72,7 @@ const chartTheme: PartialTheme = { // Log based axis cannot start a 0. Use a small positive number instead. const yAxisDomain = { - min: 0.00001, + min: 0.9, }; interface CorrelationsChartProps { @@ -135,7 +135,18 @@ export function CorrelationsChart({ if (!Array.isArray(overallHistogram)) return
; const annotationsDataValues: LineAnnotationDatum[] = [ - { dataValue: markerValue, details: `${markerPercentile}th percentile` }, + { + dataValue: markerValue, + details: i18n.translate( + 'xpack.apm.correlations.latency.chart.percentileMarkerLabel', + { + defaultMessage: '{markerPercentile}th percentile', + values: { + markerPercentile, + }, + } + ), + }, ]; const xMax = Math.max(...overallHistogram.map((d) => d.key)) ?? 0; diff --git a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx index b90274c6cbc32..7b9c8461221d6 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx @@ -356,7 +356,8 @@ export function MlLatencyCorrelations({ onClose }: Props) { {i18n.translate( 'xpack.apm.correlations.latencyCorrelations.chartTitle', { - defaultMessage: 'Latency distribution for {name}', + defaultMessage: + 'Latency distribution for {name} (Log-Log Plot)', values: { name: transactionName ?? serviceName, }, diff --git a/x-pack/plugins/canvas/__fixtures__/function_specs.ts b/x-pack/plugins/canvas/__fixtures__/function_specs.ts index ff6c564392080..a307d420e888a 100644 --- a/x-pack/plugins/canvas/__fixtures__/function_specs.ts +++ b/x-pack/plugins/canvas/__fixtures__/function_specs.ts @@ -9,8 +9,10 @@ import { functions as browserFns } from '../canvas_plugin_src/functions/browser' import { ExpressionFunction } from '../../../../src/plugins/expressions'; import { initFunctions } from '../public/functions'; import { functionSpecs as shapeFunctionSpecs } from '../../../../src/plugins/expression_shape/__fixtures__'; +import { functionSpecs as imageFunctionSpecs } from '../../../../src/plugins/expression_image/__fixtures__'; +import { functionSpecs as metricFunctionSpecs } from '../../../../src/plugins/expression_metric/__fixtures__'; export const functionSpecs = browserFns .concat(...(initFunctions({} as any) as any)) .map((fn) => new ExpressionFunction(fn())) - .concat(...shapeFunctionSpecs); + .concat(...shapeFunctionSpecs, ...imageFunctionSpecs, ...metricFunctionSpecs); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js index dcb6035dbb687..823ee074a9ddc 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js @@ -5,9 +5,11 @@ * 2.0. */ -import { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib'; +import { + functionWrapper, + fontStyle, +} from '../../../../../../src/plugins/presentation_util/common/lib'; import { testTable } from '../common/__fixtures__/test_tables'; -import { fontStyle } from '../common/__fixtures__/test_styles'; import { markdown } from './markdown'; describe('markdown', () => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts index 3de3275ebb231..a34930a5d18a8 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts @@ -33,7 +33,6 @@ import { joinRows } from './join_rows'; import { lt } from './lt'; import { lte } from './lte'; import { mapCenter } from './map_center'; -import { metric } from './metric'; import { neq } from './neq'; import { ply } from './ply'; import { progress } from './progress'; @@ -82,7 +81,6 @@ export const functions = [ lte, joinRows, mapCenter, - metric, neq, ply, progress, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/metric.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/metric.ts deleted file mode 100644 index 3b9d5216dd76b..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/metric.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { openSans } from '../../../common/lib/fonts'; -import { Render, Style, ExpressionFunctionDefinition } from '../../../types'; -import { getFunctionHelp } from '../../../i18n'; - -type Input = number | string | null; - -interface Arguments { - label: string; - metricFont: Style; - metricFormat: string; - labelFont: Style; -} - -export function metric(): ExpressionFunctionDefinition< - 'metric', - Input, - Arguments, - Render -> { - const { help, args: argHelp } = getFunctionHelp().metric; - - return { - name: 'metric', - aliases: [], - type: 'render', - inputTypes: ['number', 'string', 'null'], - help, - args: { - label: { - types: ['string'], - aliases: ['_', 'text', 'description'], - help: argHelp.label, - default: '""', - }, - labelFont: { - types: ['style'], - help: argHelp.labelFont, - default: `{font size=14 family="${openSans.value}" color="#000000" align=center}`, - }, - metricFont: { - types: ['style'], - help: argHelp.metricFont, - default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`, - }, - metricFormat: { - types: ['string'], - aliases: ['format'], - help: argHelp.metricFormat, - }, - }, - fn: (input, { label, labelFont, metricFont, metricFormat }) => { - return { - type: 'render', - as: 'metric', - value: { - metric: input === null ? '?' : input, - label, - labelFont, - metricFont, - metricFormat, - }, - }; - }, - }; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/progress.test.js b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/progress.test.js index 5f44428abf03a..567421a969d92 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/progress.test.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/progress.test.js @@ -6,10 +6,12 @@ */ import expect from '@kbn/expect'; -import { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib'; +import { + functionWrapper, + fontStyle, +} from '../../../../../../src/plugins/presentation_util/common/lib'; import { getFunctionErrors } from '../../../i18n'; import { progress } from './progress'; -import { fontStyle } from './__fixtures__/test_styles'; const errors = getFunctionErrors().progress; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/table.test.js b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/table.test.js index 42e5150b03637..e04b4a4cc1469 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/table.test.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/table.test.js @@ -5,9 +5,11 @@ * 2.0. */ -import { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib'; +import { + functionWrapper, + fontStyle, +} from '../../../../../../src/plugins/presentation_util/common/lib'; import { testTable } from './__fixtures__/test_tables'; -import { fontStyle } from './__fixtures__/test_styles'; import { table } from './table'; describe('table', () => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts index 80ca5e68860b4..5ec2af8d7773f 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts @@ -6,13 +6,12 @@ */ import { markdown } from './markdown'; -import { metric } from './metric'; import { pie } from './pie'; import { plot } from './plot'; import { progress } from './progress'; import { text } from './text'; import { table } from './table'; -export const renderFunctions = [markdown, metric, pie, plot, progress, table, text]; +export const renderFunctions = [markdown, pie, plot, progress, table, text]; export const renderFunctionFactories = []; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index eab3b88b0fe26..57d5ae69c99bb 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -6,16 +6,18 @@ */ import { imageRenderer } from '../../../../../src/plugins/expression_image/public'; +import { metricRenderer } from '../../../../../src/plugins/expression_metric/public'; import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public'; import { repeatImageRenderer } from '../../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer } from '../../../../../src/plugins/expression_shape/public'; export const renderFunctions = [ - revealImageRenderer, debugRenderer, errorRenderer, imageRenderer, + metricRenderer, + revealImageRenderer, shapeRenderer, repeatImageRenderer, ]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/__stories__/metric.stories.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/__stories__/metric.stories.tsx deleted file mode 100644 index e127018411166..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/__stories__/metric.stories.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { storiesOf } from '@storybook/react'; -import React, { CSSProperties } from 'react'; -import { Metric } from '../metric'; - -const labelFontSpec: CSSProperties = { - fontFamily: "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif", - fontWeight: 'normal', - fontStyle: 'italic', - textDecoration: 'none', - textAlign: 'center', - fontSize: '24px', - lineHeight: '1', - color: '#000000', -}; - -const metricFontSpec: CSSProperties = { - fontFamily: - "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif", - fontWeight: 'bold', - fontStyle: 'normal', - textDecoration: 'none', - textAlign: 'center', - fontSize: '48px', - lineHeight: '1', - color: '#b83c6f', -}; - -storiesOf('renderers/Metric', module) - .addDecorator((story) => ( -
- {story()} -
- )) - .add('with null metric', () => ) - .add('with number metric', () => ( - - )) - .add('with string metric', () => ( - - )) - .add('with label', () => ( - - )) - .add('with number metric and a specified format', () => ( - - )) - .add('with formatted string metric and a specified format', () => ( - - )) - .add('with invalid metricFont', () => ( - - )); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/index.tsx deleted file mode 100644 index b3c0830c3849d..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { CSSProperties } from 'react'; -import ReactDOM from 'react-dom'; -import { RendererFactory, Style } from '../../../types'; -import { RendererStrings } from '../../../i18n'; -import { Metric } from './component/metric'; - -const { metric: strings } = RendererStrings; - -export interface Config { - /** The text to display under the metric */ - label: string; - /** Font settings for the label */ - labelFont: Style; - /** Value of the metric to display */ - metric: string | number | null; - /** Font settings for the metric */ - metricFont: Style; - /** NumeralJS format string */ - metricFormat: string; -} - -export const metric: RendererFactory = () => ({ - name: 'metric', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render(domNode, config, handlers) { - ReactDOM.render( - , - domNode, - () => handlers.done() - ); - - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); diff --git a/x-pack/plugins/canvas/i18n/functions/dict/metric.ts b/x-pack/plugins/canvas/i18n/functions/dict/metric.ts deleted file mode 100644 index 44b9311fb3543..0000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/metric.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { metric } from '../../../canvas_plugin_src/functions/common/metric'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; -import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../../constants'; - -export const help: FunctionHelp> = { - help: i18n.translate('xpack.canvas.functions.metricHelpText', { - defaultMessage: 'Displays a number over a label.', - }), - args: { - label: i18n.translate('xpack.canvas.functions.metric.args.labelHelpText', { - defaultMessage: 'The text describing the metric.', - }), - labelFont: i18n.translate('xpack.canvas.functions.metric.args.labelFontHelpText', { - defaultMessage: - 'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.', - values: { - CSS, - FONT_FAMILY, - FONT_WEIGHT, - }, - }), - metricFont: i18n.translate('xpack.canvas.functions.metric.args.metricFontHelpText', { - defaultMessage: - 'The {CSS} font properties for the metric. For example, {FONT_FAMILY} or {FONT_WEIGHT}.', - values: { - CSS, - FONT_FAMILY, - FONT_WEIGHT, - }, - }), - // TODO: Find a way to generate the docs URL here - metricFormat: i18n.translate('xpack.canvas.functions.metric.args.metricFormatHelpText', { - defaultMessage: 'A {NUMERALJS} format string. For example, {example1} or {example2}.', - values: { - example1: '`"0.0a"`', - example2: '`"0%"`', - NUMERALJS, - }, - }), - }, -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index bb5681264efc9..08acecad37796 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -46,7 +46,6 @@ import { help as lt } from './dict/lt'; import { help as lte } from './dict/lte'; import { help as mapCenter } from './dict/map_center'; import { help as markdown } from './dict/markdown'; -import { help as metric } from './dict/metric'; import { help as neq } from './dict/neq'; import { help as pie } from './dict/pie'; import { help as plot } from './dict/plot'; @@ -203,7 +202,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ lte, mapCenter, markdown, - metric, neq, pie, plot, diff --git a/x-pack/plugins/canvas/i18n/renderers.ts b/x-pack/plugins/canvas/i18n/renderers.ts index faa43fe03817e..03e0ab3a19ae3 100644 --- a/x-pack/plugins/canvas/i18n/renderers.ts +++ b/x-pack/plugins/canvas/i18n/renderers.ts @@ -69,16 +69,6 @@ export const RendererStrings = { }, }), }, - metric: { - getDisplayName: () => - i18n.translate('xpack.canvas.renderer.metric.displayName', { - defaultMessage: 'Metric', - }), - getHelpDescription: () => - i18n.translate('xpack.canvas.renderer.metric.helpDescription', { - defaultMessage: 'Render a number over a label', - }), - }, pie: { getDisplayName: () => i18n.translate('xpack.canvas.renderer.pie.displayName', { diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index acede1c9c2aa8..263284564de80 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -12,6 +12,7 @@ "embeddable", "expressionError", "expressionImage", + "expressionMetric", "expressionRepeatImage", "expressionRevealImage", "expressionShape", diff --git a/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js b/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js index 14f47553002ac..e47ccafe8b6e7 100644 --- a/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js +++ b/x-pack/plugins/canvas/public/components/arg_form/advanced_failure.js @@ -5,15 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; -import { compose, withProps, withPropsOnChange } from 'recompose'; import { EuiTextArea, EuiButton, EuiButtonEmpty, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { fromExpression, toExpression } from '@kbn/interpreter/common'; -import { createStatefulPropHoc } from '../../components/enhance/stateful_prop'; - const strings = { getApplyButtonLabel: () => i18n.translate('xpack.canvas.argFormAdvancedFailure.applyButtonLabel', { @@ -29,22 +26,30 @@ const strings = { }), }; -export const AdvancedFailureComponent = (props) => { - const { - onValueChange, - defaultValue, - argExpression, - updateArgExpression, - resetErrorState, - valid, - argId, - } = props; +const isValid = (argExpression) => { + try { + fromExpression(argExpression, 'argument'); + return true; + } catch (e) { + return false; + } +}; + +export const AdvancedFailure = (props) => { + const { onValueChange, defaultValue, argValue, resetErrorState, argId } = props; + + const [argExpression, setArgExpression] = useState(toExpression(argValue, 'argument')); + const [valid, setValid] = useState(isValid(argExpression)); + + useEffect(() => { + const argExpr = toExpression(argValue, 'argument'); + setArgExpression(argExpr); + setValid(isValid(argExpr)); + }, [argValue]); const valueChange = (ev) => { ev.preventDefault(); - resetErrorState(); // when setting a new value, attempt to reset the error state - if (valid) { return onValueChange(fromExpression(argExpression.trim(), 'argument')); } @@ -69,7 +74,7 @@ export const AdvancedFailureComponent = (props) => { isInvalid={!valid} value={argExpression} compressed - onChange={updateArgExpression} + onChange={(ev) => setArgExpression(ev.target.value)} rows={3} /> @@ -89,33 +94,10 @@ export const AdvancedFailureComponent = (props) => { ); }; -AdvancedFailureComponent.propTypes = { +AdvancedFailure.propTypes = { defaultValue: PropTypes.string, onValueChange: PropTypes.func.isRequired, - argExpression: PropTypes.string.isRequired, - updateArgExpression: PropTypes.func.isRequired, resetErrorState: PropTypes.func.isRequired, - valid: PropTypes.bool.isRequired, argId: PropTypes.string.isRequired, -}; - -export const AdvancedFailure = compose( - withProps(({ argValue }) => ({ - argExpression: toExpression(argValue, 'argument'), - })), - createStatefulPropHoc('argExpression', 'updateArgExpression'), - withPropsOnChange(['argExpression'], ({ argExpression }) => ({ - valid: (function () { - try { - fromExpression(argExpression, 'argument'); - return true; - } catch (e) { - return false; - } - })(), - })) -)(AdvancedFailureComponent); - -AdvancedFailure.propTypes = { argValue: PropTypes.any.isRequired, }; diff --git a/x-pack/plugins/canvas/public/components/arg_form/index.js b/x-pack/plugins/canvas/public/components/arg_form/index.js index 84a5975cbdfb5..5dbc6c33db706 100644 --- a/x-pack/plugins/canvas/public/components/arg_form/index.js +++ b/x-pack/plugins/canvas/public/components/arg_form/index.js @@ -5,29 +5,43 @@ * 2.0. */ +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { compose, withState, lifecycle } from 'recompose'; +import { useSelector } from 'react-redux'; import { getAssets } from '../../state/selectors/assets'; import { getWorkpadInfo } from '../../state/selectors/workpad'; import { ArgForm as Component } from './arg_form'; -export const ArgForm = compose( - withState('label', 'setLabel', ({ label, argTypeInstance }) => { - return label || argTypeInstance.displayName || argTypeInstance.name; - }), - withState('resolvedArgValue', 'setResolvedArgValue'), - withState('renderError', 'setRenderError', false), - lifecycle({ - componentDidUpdate(prevProps) { - if (prevProps.templateProps.argValue !== this.props.templateProps.argValue) { - this.props.setRenderError(false); - this.props.setResolvedArgValue(); - } - }, - }), - connect((state) => ({ workpad: getWorkpadInfo(state), assets: getAssets(state) })) -)(Component); +const getLabel = (label, argTypeInstance) => + label || argTypeInstance.displayName || argTypeInstance.name; + +export const ArgForm = (props) => { + const { argTypeInstance, label: labelFromProps, templateProps } = props; + const [label, setLabel] = useState(getLabel(labelFromProps, argTypeInstance)); + const [resolvedArgValue, setResolvedArgValue] = useState(null); + const [renderError, setRenderError] = useState(false); + const workpad = useSelector(getWorkpadInfo); + const assets = useSelector(getAssets); + + useEffect(() => { + setRenderError(false); + setResolvedArgValue(); + }, [templateProps?.argValue]); + + return ( + + ); +}; ArgForm.propTypes = { label: PropTypes.string, diff --git a/x-pack/plugins/canvas/public/components/element_content/element_content.js b/x-pack/plugins/canvas/public/components/element_content/element_content.js index ce5dd1a990261..ee0ce5193102e 100644 --- a/x-pack/plugins/canvas/public/components/element_content/element_content.js +++ b/x-pack/plugins/canvas/public/components/element_content/element_content.js @@ -7,7 +7,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { pure, compose, branch, renderComponent } from 'recompose'; import Style from 'style-it'; import { getType } from '@kbn/interpreter/common'; import { Loading } from '../loading'; @@ -16,40 +15,34 @@ import { ElementShareContainer } from '../element_share_container'; import { InvalidExpression } from './invalid_expression'; import { InvalidElementType } from './invalid_element_type'; -/* - Branches - Short circut rendering of the element if the element isn't ready or isn't valid. -*/ -const branches = [ - // no renderable or renderable config value, render loading - branch( - ({ renderable, state }) => { - return !state || !renderable; - }, - renderComponent(({ backgroundColor }) => ) - ), +const isLoading = (renderable, state) => !state || !renderable; - // renderable is available, but no matching element is found, render invalid - branch(({ renderable, renderFunction }) => { - return renderable && getType(renderable) !== 'render' && !renderFunction; - }, renderComponent(InvalidElementType)), +const isNotValidForRendering = (renderable, renderFunction) => + renderable && getType(renderable) !== 'render' && !renderFunction; - // error state, render invalid expression notice - branch(({ renderable, renderFunction, state }) => { - return ( - state === 'error' || // The renderable has an error - getType(renderable) !== 'render' || // The renderable isn't, well, renderable - !renderFunction // We can't find an element in the registry for this - ); - }, renderComponent(InvalidExpression)), -]; +const isNotValidExpression = (renderable, renderFunction, state) => + state === 'error' || // The renderable has an error + getType(renderable) !== 'render' || // The renderable isn't, well, renderable + !renderFunction; // We can't find an element in the registry for this -export const ElementContent = compose( - pure, - ...branches -)(({ renderable, renderFunction, width, height, handlers }) => { +export const ElementContent = (props) => { + const { renderable, renderFunction, width, height, handlers, backgroundColor, state } = props; const { onComplete } = handlers; + if (isLoading(renderable, state)) { + return ; + } + + // renderable is available, but no matching element is found, render invalid + if (isNotValidForRendering(renderable, renderFunction)) { + return ; + } + + // error state, render invalid expression notice + if (isNotValidExpression(renderable, renderFunction, state)) { + return ; + } + return Style.it( renderable.css,
); -}); +}; ElementContent.propTypes = { renderable: PropTypes.shape({ diff --git a/x-pack/plugins/canvas/public/functions/pie.test.js b/x-pack/plugins/canvas/public/functions/pie.test.js index f1ce8786f2c66..ef180181701c9 100644 --- a/x-pack/plugins/canvas/public/functions/pie.test.js +++ b/x-pack/plugins/canvas/public/functions/pie.test.js @@ -6,9 +6,8 @@ */ import { testPie } from '../../canvas_plugin_src/functions/common/__fixtures__/test_pointseries'; -import { functionWrapper } from '../../../../../src/plugins/presentation_util/public'; +import { functionWrapper, fontStyle } from '../../../../../src/plugins/presentation_util/public'; import { - fontStyle, grayscalePalette, seriesStyle, } from '../../canvas_plugin_src/functions/common/__fixtures__/test_styles'; diff --git a/x-pack/plugins/canvas/public/functions/plot.test.js b/x-pack/plugins/canvas/public/functions/plot.test.js index a2eef889aa07e..b354c4c02b2f6 100644 --- a/x-pack/plugins/canvas/public/functions/plot.test.js +++ b/x-pack/plugins/canvas/public/functions/plot.test.js @@ -5,10 +5,9 @@ * 2.0. */ -import { functionWrapper } from '../../../../../src/plugins/presentation_util/public'; +import { functionWrapper, fontStyle } from '../../../../../src/plugins/presentation_util/public'; import { testPlot } from '../../canvas_plugin_src/functions/common/__fixtures__/test_pointseries'; import { - fontStyle, grayscalePalette, yAxisConfig, xAxisConfig, diff --git a/x-pack/plugins/canvas/public/functions/plot/get_font_spec.test.js b/x-pack/plugins/canvas/public/functions/plot/get_font_spec.test.js index e795ce82e2344..7a142a02412c2 100644 --- a/x-pack/plugins/canvas/public/functions/plot/get_font_spec.test.js +++ b/x-pack/plugins/canvas/public/functions/plot/get_font_spec.test.js @@ -4,8 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { fontStyle } from '../../../canvas_plugin_src/functions/common/__fixtures__/test_styles'; +import { fontStyle } from '../../../../../../src/plugins/presentation_util/common/lib'; import { defaultSpec, getFontSpec } from './get_font_spec'; describe('getFontSpec', () => { diff --git a/x-pack/plugins/canvas/public/index.ts b/x-pack/plugins/canvas/public/index.ts index 3fc212a060cd8..a15b15fcae333 100644 --- a/x-pack/plugins/canvas/public/index.ts +++ b/x-pack/plugins/canvas/public/index.ts @@ -18,4 +18,5 @@ export interface WithKibanaProps { }; } -export const plugin = (_initializerContext: PluginInitializerContext) => new CanvasPlugin(); +export const plugin = (initializerContext: PluginInitializerContext) => + new CanvasPlugin(initializerContext); diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index 101f64e53b685..2df5bf56e2782 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -15,6 +15,7 @@ import { AppMountParameters, AppUpdater, DEFAULT_APP_CATEGORIES, + PluginInitializerContext, } from '../../../../src/core/public'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { initLoadingIndicator } from './lib/loading_indicator'; @@ -73,6 +74,11 @@ export type CanvasStart = void; export class CanvasPlugin implements Plugin { private appUpdater = new BehaviorSubject(() => ({})); + private initContext: PluginInitializerContext; + + constructor(initContext: PluginInitializerContext) { + this.initContext = initContext; + } public setup(coreSetup: CoreSetup, setupPlugins: CanvasSetupDeps) { const { api: canvasApi, registries } = getPluginApi(setupPlugins.expressions); @@ -105,7 +111,9 @@ export class CanvasPlugin srcPlugin.start(coreStart, startPlugins); const { pluginServices } = await import('./services'); - pluginServices.setRegistry(pluginServiceRegistry.start({ coreStart, startPlugins })); + pluginServices.setRegistry( + pluginServiceRegistry.start({ coreStart, startPlugins, initContext: this.initContext }) + ); // Load application bundle const { renderApp, initializeCanvas, teardownCanvas } = await import('./application'); diff --git a/x-pack/plugins/canvas/public/services/kibana/platform.ts b/x-pack/plugins/canvas/public/services/kibana/platform.ts index 79eae8d8081bb..f71d480b552f6 100644 --- a/x-pack/plugins/canvas/public/services/kibana/platform.ts +++ b/x-pack/plugins/canvas/public/services/kibana/platform.ts @@ -15,12 +15,17 @@ export type CanvaPlatformServiceFactory = KibanaPluginServiceFactory< CanvasStartDeps >; -export const platformServiceFactory: CanvaPlatformServiceFactory = ({ coreStart }) => { +export const platformServiceFactory: CanvaPlatformServiceFactory = ({ coreStart, initContext }) => { + if (!initContext) { + throw new Error('Canvas platform service requires init context'); + } + return { getBasePath: coreStart.http.basePath.get, getBasePathInterface: () => coreStart.http.basePath, getElasticWebsiteUrl: () => coreStart.docLinks.ELASTIC_WEBSITE_URL, getDocLinkVersion: () => coreStart.docLinks.DOC_LINK_VERSION, + getKibanaVersion: () => initContext.env.packageInfo.version, // TODO: is there a better type for this? The capabilities type allows for a Record, // though we don't do this. So this cast may be the best option. getHasWriteAccess: () => coreStart.application.capabilities.canvas.save as boolean, diff --git a/x-pack/plugins/canvas/public/services/platform.ts b/x-pack/plugins/canvas/public/services/platform.ts index 7a452d809a614..b622484c16fc3 100644 --- a/x-pack/plugins/canvas/public/services/platform.ts +++ b/x-pack/plugins/canvas/public/services/platform.ts @@ -19,6 +19,7 @@ export interface CanvasPlatformService { getBasePathInterface: () => IBasePath; getDocLinkVersion: () => string; getElasticWebsiteUrl: () => string; + getKibanaVersion: () => string; getHasWriteAccess: () => boolean; getUISetting: (key: string, defaultValue?: any) => any; setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void; diff --git a/x-pack/plugins/canvas/public/services/stubs/platform.ts b/x-pack/plugins/canvas/public/services/stubs/platform.ts index 181d355df8a1c..1daaa6bd32f09 100644 --- a/x-pack/plugins/canvas/public/services/stubs/platform.ts +++ b/x-pack/plugins/canvas/public/services/stubs/platform.ts @@ -24,6 +24,7 @@ export const platformServiceFactory: CanvasPlatformServiceFactory = () => ({ getBasePathInterface: noop, getDocLinkVersion: () => 'dockLinkVersion', getElasticWebsiteUrl: () => 'https://elastic.co', + getKibanaVersion: () => 'kibanaVersion', getHasWriteAccess: () => true, getUISetting, setBreadcrumbs: noop, diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index 9b86ebddbd9b9..0217351af9cb6 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -6,7 +6,6 @@ */ import { markdown } from '../canvas_plugin_src/renderers/markdown'; -import { metric } from '../canvas_plugin_src/renderers/metric'; import { pie } from '../canvas_plugin_src/renderers/pie'; import { plot } from '../canvas_plugin_src/renderers/plot'; import { progress } from '../canvas_plugin_src/renderers/progress'; @@ -20,6 +19,7 @@ import { import { repeatImageRenderer as repeatImage } from '../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer as shape } from '../../../../src/plugins/expression_shape/public'; +import { metricRenderer as metric } from '../../../../src/plugins/expression_metric/public'; /** * This is a collection of renderers which are bundled with the runtime. If diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index 6d57aee565c89..384a7716657f5 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -33,6 +33,7 @@ { "path": "../../../src/plugins/expressions/tsconfig.json" }, { "path": "../../../src/plugins/expression_error/tsconfig.json" }, { "path": "../../../src/plugins/expression_image/tsconfig.json" }, + { "path": "../../../src/plugins/expression_metric/tsconfig.json" }, { "path": "../../../src/plugins/expression_repeat_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_shape/tsconfig.json" }, diff --git a/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx index 6f02a209f22dc..983e32ba508fb 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx @@ -5,14 +5,16 @@ * 2.0. */ -import { ConnectorTypes } from '../../../../common'; +import { ActionTypeConnector, ConnectorTypes } from '../../../../common'; import { ActionConnector } from '../../../containers/configure/types'; import { UseConnectorsResponse } from '../../../containers/configure/use_connectors'; import { ReturnUseCaseConfigure } from '../../../containers/configure/use_configure'; import { UseActionTypesResponse } from '../../../containers/configure/use_action_types'; import { connectorsMock, actionTypesMock } from '../../../containers/configure/mock'; export { mappings } from '../../../containers/configure/mock'; + export const connectors: ActionConnector[] = connectorsMock; +export const actionTypes: ActionTypeConnector[] = actionTypesMock; export const searchURL = '?timerange=(global:(linkTo:!(),timerange:(from:1585487656371,fromStr:now-24h,kind:relative,to:1585574056371,toStr:now)),timeline:(linkTo:!(),timerange:(from:1585227005527,kind:absolute,to:1585313405527)))'; diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx index e5b1c66d01e1a..0bda6fe185093 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx @@ -11,7 +11,7 @@ import { mount, ReactWrapper } from 'enzyme'; import { Connectors, Props } from './connectors'; import { TestProviders } from '../../common/mock'; import { ConnectorsDropdown } from './connectors_dropdown'; -import { connectors } from './__mock__'; +import { connectors, actionTypes } from './__mock__'; import { ConnectorTypes } from '../../../common'; import { useKibana } from '../../common/lib/kibana'; @@ -24,6 +24,7 @@ describe('Connectors', () => { const handleShowEditFlyout = jest.fn(); const props: Props = { + actionTypes, connectors, disabled: false, handleShowEditFlyout, diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx index 45be02e05e1f0..40f314a653882 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx @@ -21,7 +21,7 @@ import * as i18n from './translations'; import { ActionConnector, CaseConnectorMapping } from '../../containers/configure/types'; import { Mapping } from './mapping'; -import { ConnectorTypes } from '../../../common'; +import { ActionTypeConnector, ConnectorTypes } from '../../../common'; const EuiFormRowExtended = styled(EuiFormRow)` .euiFormRow__labelWrapper { @@ -32,6 +32,7 @@ const EuiFormRowExtended = styled(EuiFormRow)` `; export interface Props { + actionTypes: ActionTypeConnector[]; connectors: ActionConnector[]; disabled: boolean; handleShowEditFlyout: () => void; @@ -42,6 +43,7 @@ export interface Props { updateConnectorDisabled: boolean; } const ConnectorsComponent: React.FC = ({ + actionTypes, connectors, disabled, handleShowEditFlyout, @@ -56,6 +58,11 @@ const ConnectorsComponent: React.FC = ({ [connectors, selectedConnector.id] ); + const actionTypeName = useMemo( + () => actionTypes.find((c) => c.id === selectedConnector.type)?.name ?? 'Unknown', + [actionTypes, selectedConnector.type] + ); + const dropDownLabel = useMemo( () => ( @@ -103,7 +110,7 @@ const ConnectorsComponent: React.FC = ({ {selectedConnector.type !== ConnectorTypes.none ? ( diff --git a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx index 73f48654b2299..8d235f31277fe 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx @@ -12,24 +12,16 @@ import { FieldMapping, FieldMappingProps } from './field_mapping'; import { mappings } from './__mock__'; import { TestProviders } from '../../common/mock'; import { FieldMappingRowStatic } from './field_mapping_row_static'; -import { useKibana } from '../../common/lib/kibana'; - -jest.mock('../../common/lib/kibana'); -const useKibanaMock = useKibana as jest.Mocked; describe('FieldMappingRow', () => { let wrapper: ReactWrapper; const props: FieldMappingProps = { + actionTypeName: 'ServiceNow ITSM', isLoading: false, mappings, - connectorActionTypeId: '.servicenow', }; beforeAll(() => { - useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest.fn().mockReturnValue({ - actionTypeTitle: '.servicenow', - iconClass: 'logoSecurity', - }); wrapper = mount(, { wrappingComponent: TestProviders }); }); @@ -61,4 +53,13 @@ describe('FieldMappingRow', () => { expect(row.prop('selectedThirdParty')).toEqual(mappings[index].target); }); }); + + test('displays the label of the second column correctly', () => { + expect( + wrapper + .find('[data-test-subj="case-configure-field-mappings-second-col-label"]') + .first() + .text() + ).toBe('ServiceNow ITSM field'); + }); }); diff --git a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx b/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx index b5ce978a1b481..fe99f718c1401 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React from 'react'; import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import styled from 'styled-components'; @@ -13,7 +13,6 @@ import { FieldMappingRowStatic } from './field_mapping_row_static'; import * as i18n from './translations'; import { CaseConnectorMapping } from '../../containers/configure/types'; -import { useKibana } from '../../common/lib/kibana'; const FieldRowWrapper = styled.div` margin: 10px 0; @@ -21,22 +20,16 @@ const FieldRowWrapper = styled.div` `; export interface FieldMappingProps { - connectorActionTypeId: string; + actionTypeName: string; isLoading: boolean; mappings: CaseConnectorMapping[]; } const FieldMappingComponent: React.FC = ({ - connectorActionTypeId, + actionTypeName, isLoading, mappings, }) => { - const { triggersActionsUi } = useKibana().services; - const selectedConnector = useMemo( - () => triggersActionsUi.actionTypeRegistry.get(connectorActionTypeId) ?? { fields: {} }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [connectorActionTypeId] - ); return mappings.length ? ( @@ -45,10 +38,8 @@ const FieldMappingComponent: React.FC = ({ {i18n.FIELD_MAPPING_FIRST_COL} - - - {i18n.FIELD_MAPPING_SECOND_COL(selectedConnector.actionTypeTitle ?? '')} - + + {i18n.FIELD_MAPPING_SECOND_COL(actionTypeName)} {i18n.FIELD_MAPPING_THIRD_COL} diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx index ac43ec05319a0..d4e2ad36d4957 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.tsx @@ -205,6 +205,7 @@ const ConfigureCasesComponent: React.FC> = ({ ; describe('Mapping', () => { const props: MappingProps = { - connectorActionTypeId: '.servicenow', + actionTypeName: 'ServiceNow ITSM', isLoading: false, mappings, }; - beforeEach(() => { - jest.clearAllMocks(); - useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest.fn().mockReturnValue({ - actionTypeTitle: 'ServiceNow ITSM', - iconClass: 'logoSecurity', - }); - }); - test('it shows mapping form group', () => { const wrapper = mount(, { wrappingComponent: TestProviders }); expect(wrapper.find('[data-test-subj="static-mappings"]').first().exists()).toBe(true); @@ -45,6 +33,21 @@ describe('Mapping', () => { 'short_description' ); }); + + test('displays the title correctly', () => { + const wrapper = mount(, { wrappingComponent: TestProviders }); + expect(wrapper.find('[data-test-subj="field-mapping-text"] h4').first().text()).toBe( + 'ServiceNow ITSM field mappings' + ); + }); + + test('displays the description correctly', () => { + const wrapper = mount(, { wrappingComponent: TestProviders }); + expect(wrapper.find('[data-test-subj="field-mapping-desc"]').first().text()).toBe( + 'Map Case fields to ServiceNow ITSM fields when pushing data to ServiceNow ITSM. Field mappings require an established connection to ServiceNow ITSM.' + ); + }); + test('displays connection warning when isLoading: false and mappings: []', () => { const wrapper = mount(, { wrappingComponent: TestProviders, diff --git a/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx b/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx index 16c02606ae90b..eb14c22b900c4 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx @@ -14,42 +14,32 @@ import * as i18n from './translations'; import { FieldMapping } from './field_mapping'; import { CaseConnectorMapping } from '../../containers/configure/types'; -import { useKibana } from '../../common/lib/kibana'; export interface MappingProps { - connectorActionTypeId: string; + actionTypeName: string; isLoading: boolean; mappings: CaseConnectorMapping[]; } -const MappingComponent: React.FC = ({ - connectorActionTypeId, - isLoading, - mappings, -}) => { - const { triggersActionsUi } = useKibana().services; - const selectedConnector = useMemo( - () => triggersActionsUi.actionTypeRegistry.get(connectorActionTypeId), - [connectorActionTypeId, triggersActionsUi] - ); +const MappingComponent: React.FC = ({ actionTypeName, isLoading, mappings }) => { const fieldMappingDesc: { desc: string; color: TextColor } = useMemo( () => mappings.length > 0 || isLoading ? { - desc: i18n.FIELD_MAPPING_DESC(selectedConnector.actionTypeTitle ?? ''), + desc: i18n.FIELD_MAPPING_DESC(actionTypeName), color: 'subdued', } : { - desc: i18n.FIELD_MAPPING_DESC_ERR(selectedConnector.actionTypeTitle ?? ''), + desc: i18n.FIELD_MAPPING_DESC_ERR(actionTypeName), color: 'danger', }, - [isLoading, mappings.length, selectedConnector.actionTypeTitle] + [isLoading, mappings.length, actionTypeName] ); return ( - -

{i18n.FIELD_MAPPING_TITLE(selectedConnector.actionTypeTitle ?? '')}

+ +

{i18n.FIELD_MAPPING_TITLE(actionTypeName)}

{fieldMappingDesc.desc} @@ -57,7 +47,7 @@ const MappingComponent: React.FC = ({
= memo( ({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => { const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - const [routerHistoryInstance] = useState(routerHistory || createHashHistory()); - // Sync our hash history with Kibana scoped history - useEffect(() => { - const unlistenParentHistory = history.listen(() => { - const newHash = createHashHistory(); - if (newHash.location.pathname !== routerHistoryInstance.location.pathname) { - routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || ''); - } - }); - - return unlistenParentHistory; - }, [history, routerHistoryInstance]); return ( - - - - - - - - - - + + + + + + + + + + {children} - - - - - - - - - + + + + + + + + + ); } ); @@ -277,7 +266,7 @@ const FleetTopNav = memo( defaultMessage: 'Fleet settings', }), iconType: 'gear', - run: () => (window.location.href = getModalHref('settings')), + run: () => services.application.navigateToUrl(getModalHref('settings')), }, ]; return ( @@ -327,7 +316,26 @@ export const AppRoutes = memo( - + { + // BWC < 7.15 Fleet was using a hash router: redirect old routes using hash + const shouldRedirectHash = location.pathname === '' && location.hash.length > 0; + if (!shouldRedirectHash) { + return ; + } + const pathname = location.hash.replace(/^#(\/fleet)?/, ''); + + return ( + + ); + }} + /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx index c0c425447e556..3b0ab9c62ca11 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx @@ -150,18 +150,30 @@ const breadcrumbGetters: { }; export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { - const { chrome, http } = useStartServices(); + const { chrome, http, application } = useStartServices(); const breadcrumbs = - breadcrumbGetters[page]?.(values).map((breadcrumb) => ({ - ...breadcrumb, - href: breadcrumb.href + breadcrumbGetters[page]?.(values).map((breadcrumb) => { + const href = breadcrumb.href ? http.basePath.prepend( - `${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}#${ + `${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}${ breadcrumb.href }` ) - : undefined, - })) || []; + : undefined; + return { + ...breadcrumb, + href, + onClick: href + ? (ev: React.MouseEvent) => { + if (ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey) { + return; + } + ev.preventDefault(); + application.navigateToUrl(href); + } + : undefined, + }; + }) || []; const docTitle: string[] = [...breadcrumbs] .reverse() .map((breadcrumb) => breadcrumb.text as string); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx index 060c49a84c5aa..476a5d8e029d3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx @@ -43,7 +43,10 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ 'data-test-subj': dataTestSubj, }) => { const pageTitle = useMemo(() => { - if ((from === 'package' || from === 'package-edit' || from === 'edit') && packageInfo) { + if ( + (from === 'package' || from === 'package-edit' || from === 'edit' || from === 'policy') && + packageInfo + ) { return ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx index a624d8ced9180..c115089cccb1e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx @@ -31,7 +31,7 @@ describe('when on the package policy create page', () => { beforeEach(() => { testRenderer = createFleetTestRendererMock(); mockApiCalls(testRenderer.startServices.http); - testRenderer.history.push(createPageUrlPath); + testRenderer.mountHistory.push(createPageUrlPath); }); describe('and Route state is provided via Fleet HashRouter', () => { @@ -43,7 +43,7 @@ describe('when on the package policy create page', () => { onCancelNavigateTo: [PLUGIN_ID, { path: '/cancel/url/here' }], }; - testRenderer.history.replace({ + testRenderer.mountHistory.replace({ pathname: createPageUrlPath, state: expectedRouteState, }); @@ -72,18 +72,18 @@ describe('when on the package policy create page', () => { expect(cancelButton.href).toBe(expectedRouteState.onCancelUrl); }); - it('should redirect via Fleet HashRouter when cancel link is clicked', () => { + it('should redirect via history when cancel link is clicked', () => { act(() => { cancelLink.click(); }); - expect(testRenderer.history.location.pathname).toBe('/cancel/url/here'); + expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here'); }); - it('should redirect via Fleet HashRouter when cancel Button (button bar) is clicked', () => { + it('should redirect via history when cancel Button (button bar) is clicked', () => { act(() => { cancelButton.click(); }); - expect(testRenderer.history.location.pathname).toBe('/cancel/url/here'); + expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here'); }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx index 39340a21d349b..e19cb7b1ca5e8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx @@ -39,7 +39,7 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => { fill onClick={() => application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - path: `#${pagePathGetters.integrations_all()[1]}`, + path: pagePathGetters.integrations_all()[1], state: { forAgentPolicyId: policyId }, }) } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index 49af14b7234fa..0d2d8e1882183 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -199,7 +199,7 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ iconType="refresh" onClick={() => { application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - path: `#${pagePathGetters.integrations_all()[1]}`, + path: pagePathGetters.integrations_all()[1], state: { forAgentPolicyId: agentPolicy.id }, }); }} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx index d8db44e28e4af..19f0216a39e03 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HashRouter as Router, Switch, Route } from 'react-router-dom'; +import { Router, Switch, Route, useHistory } from 'react-router-dom'; import { FLEET_ROUTING_PATHS } from '../../constants'; import { useBreadcrumbs } from '../../hooks'; @@ -20,9 +20,10 @@ import { EditPackagePolicyPage } from './edit_package_policy_page'; export const AgentPolicyApp: React.FunctionComponent = () => { useBreadcrumbs('policies'); + const history = useHistory(); return ( - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 52a4c9d17648b..494541a00fc0a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui'; import { FLEET_ROUTING_PATHS } from '../../constants'; @@ -30,7 +30,7 @@ import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); - + const history = useHistory(); const { agents } = useConfig(); const capabilities = useCapabilities(); @@ -118,7 +118,7 @@ export const AgentsApp: React.FunctionComponent = () => { ) : undefined; return ( - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx index c660d3ed29767..51c8346a665cf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { FLEET_ROUTING_PATHS } from '../../constants'; import { DefaultLayout } from '../../layouts'; @@ -14,8 +14,10 @@ import { DefaultLayout } from '../../layouts'; import { DataStreamListPage } from './list_page'; export const DataStreamApp: React.FunctionComponent = () => { + const history = useHistory(); + return ( - + diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx index ae59d33e44b82..f0c94b51677ee 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx @@ -9,7 +9,6 @@ import React, { memo, useEffect, useState } from 'react'; import type { AppMountParameters } from 'kibana/public'; import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; -import { createHashHistory } from 'history'; import { Router, Redirect, Route, Switch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -26,7 +25,10 @@ import { import type { FleetConfigType, FleetStartServices } from '../../plugin'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../../src/plugins/kibana_react/public'; import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { AgentPolicyContextProvider, useUrlModal } from './hooks'; @@ -39,7 +41,7 @@ import type { UIExtensionsStorage } from './types'; import { EPMApp } from './sections/epm'; import { DefaultLayout, WithoutHeaderLayout } from './layouts'; import { PackageInstallProvider } from './hooks'; -import { useBreadcrumbs, IntraAppStateProvider, UIExtensionsContext } from './hooks'; +import { useBreadcrumbs, UIExtensionsContext } from './hooks'; const ErrorLayout = ({ children }: { children: JSX.Element }) => ( @@ -185,25 +187,12 @@ export const IntegrationsAppContext: React.FC<{ kibanaVersion: string; extensions: UIExtensionsStorage; /** For testing purposes only */ - routerHistory?: History; -}> = memo( - ({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => { - const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - const [routerHistoryInstance] = useState(routerHistory || createHashHistory()); - - // Sync our hash history with Kibana scoped history - useEffect(() => { - const unlistenParentHistory = history.listen(() => { - const newHash = createHashHistory(); - if (newHash.location.pathname !== routerHistoryInstance.location.pathname) { - routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || ''); - } - }); - - return unlistenParentHistory; - }, [history, routerHistoryInstance]); + routerHistory?: History; // TODO remove +}> = memo(({ children, startServices, config, history, kibanaVersion, extensions }) => { + const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - return ( + return ( + @@ -212,15 +201,13 @@ export const IntegrationsAppContext: React.FC<{ - - - - - {children} - - - - + + + + {children} + + + @@ -229,9 +216,9 @@ export const IntegrationsAppContext: React.FC<{ - ); - } -); + + ); +}); export const AppRoutes = memo(() => { const { modal, setModal } = useUrlModal(); @@ -250,7 +237,26 @@ export const AppRoutes = memo(() => { - + { + // BWC < 7.15 Fleet was using a hash router: redirect old routes using hash + const shouldRedirectHash = location.pathname === '' && location.hash.length > 0; + if (!shouldRedirectHash) { + return ; + } + const pathname = location.hash.replace(/^#/, ''); + + return ( + + ); + }} + /> ); diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx index 19f72fdc69bba..63c8f1cbd318c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx @@ -51,14 +51,23 @@ const breadcrumbGetters: { }; export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { - const { chrome, http } = useStartServices(); + const { chrome, http, application } = useStartServices(); const breadcrumbs: ChromeBreadcrumb[] = - breadcrumbGetters[page]?.(values).map((breadcrumb) => ({ - ...breadcrumb, - href: breadcrumb.href - ? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}#${breadcrumb.href}`) - : undefined, - })) || []; + breadcrumbGetters[page]?.(values).map((breadcrumb) => { + const href = breadcrumb.href + ? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}${breadcrumb.href}`) + : undefined; + return { + ...breadcrumb, + href, + onClick: href + ? (ev: React.MouseEvent) => { + ev.preventDefault(); + application.navigateToUrl(href); + } + : undefined, + }; + }) || []; const docTitle: string[] = [...breadcrumbs] .reverse() .map((breadcrumb) => breadcrumb.text as string); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx index f436c248abd3c..31a3e2164a247 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx @@ -45,22 +45,22 @@ describe('when on integration detail', () => { )); - beforeEach(() => { + beforeEach(async () => { testRenderer = createIntegrationsTestRendererMock(); mockedApi = mockApiCalls(testRenderer.startServices.http); - testRenderer.history.push(detailPageUrlPath); + act(() => testRenderer.mountHistory.push(detailPageUrlPath)); }); afterEach(() => { cleanup(); - window.location.hash = '#/'; }); describe('and the package is installed', () => { beforeEach(() => render()); it('should display agent policy usage count', async () => { - await mockedApi.waitForApi(); + await act(() => mockedApi.waitForApi()); + expect(renderResult.queryByTestId('agentPolicyCount')).not.toBeNull(); }); @@ -105,11 +105,11 @@ describe('when on integration detail', () => { it('should redirect if custom url is accessed', () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1] ); }); - expect(testRenderer.history.location.pathname).toEqual('/detail/nginx-0.3.7/overview'); + expect(testRenderer.mountHistory.location.pathname).toEqual('/detail/nginx-0.3.7/overview'); }); }); @@ -153,7 +153,7 @@ describe('when on integration detail', () => { it('should display custom content when tab is clicked', async () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1] ); }); @@ -200,7 +200,7 @@ describe('when on integration detail', () => { it('should display custom assets when tab is clicked', async () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_assets({ pkgkey: 'nginx-0.3.7' })[1] ); }); @@ -215,7 +215,7 @@ describe('when on integration detail', () => { it('should link to the create page', () => { const addButton = renderResult.getByTestId('addIntegrationPolicyButton') as HTMLAnchorElement; expect(addButton.href).toEqual( - 'http://localhost/mock/app/fleet#/integrations/nginx-0.3.7/add-integration' + 'http://localhost/mock/app/fleet/integrations/nginx-0.3.7/add-integration' ); }); }); @@ -223,7 +223,7 @@ describe('when on integration detail', () => { describe('and on the Policies Tab', () => { const policiesTabURLPath = pagePathGetters.integration_details_policies({ pkgkey })[1]; beforeEach(() => { - testRenderer.history.push(policiesTabURLPath); + testRenderer.mountHistory.push(policiesTabURLPath); render(); }); @@ -238,7 +238,7 @@ describe('when on integration detail', () => { 'integrationNameLink' )[0] as HTMLAnchorElement; expect(firstPolicy.href).toEqual( - 'http://localhost/mock/app/integrations#/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' + 'http://localhost/mock/app/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' ); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 21a139ad11baa..26869f8fea574 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -235,22 +235,18 @@ export function Detail() { redirectToPath = [ PLUGIN_ID, { - path: `#${ - pagePathGetters.policy_details({ - policyId: agentPolicyIdFromContext, - })[1] - }`, + path: pagePathGetters.policy_details({ + policyId: agentPolicyIdFromContext, + })[1], }, ]; } else { redirectToPath = [ INTEGRATIONS_PLUGIN_ID, { - path: `#${ - pagePathGetters.integration_details_policies({ - pkgkey, - })[1] - }`, + path: pagePathGetters.integration_details_policies({ + pkgkey, + })[1], }, ]; } @@ -260,16 +256,16 @@ export function Detail() { onCancelNavigateTo: [ INTEGRATIONS_PLUGIN_ID, { - path: currentPath, + path: pagePathGetters.integration_details_overview({ + pkgkey, + })[1], }, ], onCancelUrl: currentPath, }; services.application.navigateToApp(PLUGIN_ID, { - // Necessary because of Fleet's HashRouter. Can be changed when - // https://github.com/elastic/kibana/issues/96134 is resolved - path: `#${path}`, + path, state: redirectBackRouteState, }); }, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index 8ec5fd83a1254..b7f044040e43f 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -273,6 +273,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps agentPolicy={agentPolicy} packagePolicy={packagePolicy} viewDataStep={viewDataStep} + showAddAgent={true} /> ); }, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index 14f378bc379a6..3b161a375e7ce 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -53,7 +53,7 @@ const LatestVersionLink = ({ name, version }: { name: string; version: string }) pkgkey: `${name}-${version}`, }); return ( - + = ({ agentPolicy, packagePolicy, viewDataStep }) => { + showAddAgent?: boolean; +}> = ({ agentPolicy, packagePolicy, viewDataStep, showAddAgent }) => { const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); const { getHref } = useLink(); const hasWriteCapabilities = useCapabilities().write; @@ -47,19 +48,23 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ // defaultMessage="View integration" // /> // , - { - setIsActionsMenuOpen(false); - setIsEnrollmentFlyoutOpen(true); - }} - key="addAgent" - > - - , + ...(showAddAgent + ? [ + { + setIsActionsMenuOpen(false); + setIsEnrollmentFlyoutOpen(true); + }} + key="addAgent" + > + + , + ] + : []), { - forRoute: string; - routeState?: S; -} - -const IntraAppStateContext = React.createContext({ forRoute: '' }); -const wasHandled = new WeakSet(); - -/** - * Provides a bridget between Kibana's ScopedHistory instance (normally used with BrowserRouter) - * and the Hash router used within the app in order to enable state to be used between kibana - * apps - */ -export const IntraAppStateProvider = memo<{ - kibanaScopedHistory: AppMountParameters['history']; - children: React.ReactNode; -}>(({ kibanaScopedHistory, children }) => { - const internalAppToAppState = useMemo(() => { - return { - forRoute: new URL(`${kibanaScopedHistory.location.hash.substr(1)}`, 'http://localhost') - .pathname, - routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState, - }; - }, [kibanaScopedHistory.location.state, kibanaScopedHistory.location.hash]); - return ( - - {children} - - ); -}); - /** * Retrieve UI Route state from the React Router History for the current URL location. * This state can be used by other Kibana Apps to influence certain behaviours in Ingest, for example, * redirecting back to an given Application after a craete action. */ -export function useIntraAppState(): - | IntraAppState['routeState'] - | undefined { +export function useIntraAppState(): S | undefined { const location = useLocation(); - const intraAppState = useContext(IntraAppStateContext); - if (!intraAppState) { - throw new Error('Hook called outside of IntraAppStateContext'); - } - return useMemo(() => { - // Due to the use of HashRouter in Ingest, we only want state to be returned - // once so that it does not impact navigation to the page from within the - // ingest app. side affect is that the browser back button would not work - // consistently either. - - if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) { - wasHandled.add(intraAppState); - return intraAppState.routeState as S; - } - // Default is to return the state in the Fleet HashRouter, in order to enable use of route state - // that is used via Kibana's ScopedHistory from within the Fleet HashRouter (ex. things like - // `core.application.navigateTo()` - // Once this https://github.com/elastic/kibana/issues/70358 is implemented (move to BrowserHistory - // using kibana's ScopedHistory), then this work-around can be removed. - return location.state as S; - }, [intraAppState, location.pathname, location.state]); + return location.state as S; } diff --git a/x-pack/plugins/fleet/public/hooks/use_link.ts b/x-pack/plugins/fleet/public/hooks/use_link.ts index 6917e0f5c3b8e..846ca9d0fdafa 100644 --- a/x-pack/plugins/fleet/public/hooks/use_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_link.ts @@ -27,7 +27,7 @@ export const useLink = () => { core.http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/${path}`), getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => { const [basePath, path] = getSeparatePaths(page, values); - return core.http.basePath.prepend(`${basePath}#${path}`); + return core.http.basePath.prepend(`${basePath}${path}`); }, }; }; diff --git a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx index 71c9650709ee2..d0724545ee902 100644 --- a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx +++ b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx @@ -6,7 +6,7 @@ */ import type { History } from 'history'; -import { createMemoryHistory, createHashHistory } from 'history'; +import { createMemoryHistory } from 'history'; import React, { memo } from 'react'; import type { RenderOptions, RenderResult } from '@testing-library/react'; import { render as reactRender, act } from '@testing-library/react'; @@ -47,9 +47,10 @@ export const createFleetTestRendererMock = (): TestRenderer => { const basePath = '/mock'; const extensions: UIExtensionsStorage = {}; const startServices = createStartServices(basePath); + const history = createMemoryHistory({ initialEntries: [basePath] }); const testRendererMocks: TestRenderer = { - history: createHashHistory(), - mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath), + history, + mountHistory: new ScopedHistory(history, basePath), startServices, config: createConfigurationMock(), startInterface: createStartMock(extensions), @@ -89,7 +90,7 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => { const extensions: UIExtensionsStorage = {}; const startServices = createStartServices(basePath); const testRendererMocks: TestRenderer = { - history: createHashHistory(), + history: createMemoryHistory(), mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath), startServices, config: createConfigurationMock(), diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index 0606334737a2a..2c723a3269737 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -104,6 +104,7 @@ export class FleetPlugin implements Plugin { const [coreStartServices, startDepsServices] = (await core.getStartServices()) as [ CoreStart, diff --git a/x-pack/plugins/fleet/public/search_provider.test.ts b/x-pack/plugins/fleet/public/search_provider.test.ts index 521337c9dda6b..8eee18710d477 100644 --- a/x-pack/plugins/fleet/public/search_provider.test.ts +++ b/x-pack/plugins/fleet/public/search_provider.test.ts @@ -92,7 +92,7 @@ describe('Package search provider', () => { title: 'test', type: 'integration', url: { - path: 'undefined#/detail/test-test/overview', + path: 'undefined/detail/test-test/overview', prependBasePath: false, }, }, @@ -102,7 +102,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -175,7 +175,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -231,7 +231,7 @@ describe('Package search provider', () => { title: 'test', type: 'integration', url: { - path: 'undefined#/detail/test-test/overview', + path: 'undefined/detail/test-test/overview', prependBasePath: false, }, }, @@ -241,7 +241,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -274,7 +274,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, diff --git a/x-pack/plugins/fleet/public/search_provider.ts b/x-pack/plugins/fleet/public/search_provider.ts index 9705f4da50f94..5f53c0a8e44ba 100644 --- a/x-pack/plugins/fleet/public/search_provider.ts +++ b/x-pack/plugins/fleet/public/search_provider.ts @@ -45,10 +45,8 @@ const toSearchResult = ( title: pkg.title, score: 80, url: { - // TODO: See https://github.com/elastic/kibana/issues/96134 for details about why we use '#' here. Below should be updated - // as part of migrating to non-hash based router. // prettier-ignore - path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}#${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, + path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, prependBasePath: false, }, }; diff --git a/x-pack/plugins/graph/public/angular/templates/index.html b/x-pack/plugins/graph/public/angular/templates/index.html index 10bbb2e8ec6c7..14c37cab9d9fd 100644 --- a/x-pack/plugins/graph/public/angular/templates/index.html +++ b/x-pack/plugins/graph/public/angular/templates/index.html @@ -3,51 +3,14 @@ -
-
-
- -
- http://host:port/{{ selectedIndex.name }}/_graph/explore - - -
-
-
-
-
+ + + +
{ @@ -119,7 +120,7 @@ const mainTemplate = (basePath: string) => `
const moduleName = 'app/graph'; -const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap', 'ui.ace']; +const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap']; function mountGraphApp(appBasePath: string, element: HTMLElement) { const mountpoint = document.createElement('div'); diff --git a/x-pack/plugins/graph/public/components/inspect_panel/inspect_panel.tsx b/x-pack/plugins/graph/public/components/inspect_panel/inspect_panel.tsx new file mode 100644 index 0000000000000..2f29849bebcec --- /dev/null +++ b/x-pack/plugins/graph/public/components/inspect_panel/inspect_panel.tsx @@ -0,0 +1,109 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo, useState } from 'react'; +import { EuiTab, EuiTabs, EuiText } from '@elastic/eui'; +import { monaco, XJsonLang } from '@kbn/monaco'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { IUiSettingsClient } from 'kibana/public'; +import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { + CodeEditor, + KibanaContextProvider, +} from '../../../../../../src/plugins/kibana_react/public'; + +interface InspectPanelProps { + showInspect?: boolean; + indexPattern?: IndexPattern; + uiSettings: IUiSettingsClient; + lastRequest?: string; + lastResponse?: string; +} + +const CODE_EDITOR_OPTIONS: monaco.editor.IStandaloneEditorConstructionOptions = { + automaticLayout: true, + fontSize: 12, + lineNumbers: 'on', + minimap: { + enabled: false, + }, + overviewRulerBorder: false, + readOnly: true, + scrollbar: { + alwaysConsumeMouseWheel: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', +}; + +const dummyCallback = () => {}; + +export const InspectPanel = ({ + showInspect, + lastRequest, + lastResponse, + indexPattern, + uiSettings, +}: InspectPanelProps) => { + const [selectedTabId, setSelectedTabId] = useState('request'); + + const onRequestClick = () => setSelectedTabId('request'); + const onResponseClick = () => setSelectedTabId('response'); + + const services = useMemo(() => ({ uiSettings }), [uiSettings]); + + const editorContent = useMemo(() => (selectedTabId === 'request' ? lastRequest : lastResponse), [ + selectedTabId, + lastRequest, + lastResponse, + ]); + + if (showInspect) { + return ( + +
+
+
+ +
+ +
+ + http://host:port/{indexPattern?.id}/_graph/explore + + + + + + + + + + +
+
+
+
+ ); + } + + return null; +}; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index ec19e639b91c9..70671260ce5b9 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -110,6 +110,7 @@ export class GraphPlugin indexPatterns: pluginsStart.data!.indexPatterns, overlays: coreStart.overlays, savedObjects: pluginsStart.savedObjects, + uiSettings: core.uiSettings, }); }, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index a1bdbdfd14c12..fc48bd8009cf2 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -67,8 +67,8 @@ type LogThresholdAlertInstance = AlertInstance< type LogThresholdAlertInstanceFactory = ( id: string, reason: string, - threshold: number, - value: number + value: number, + threshold: number ) => LogThresholdAlertInstance; const COMPOSITE_GROUP_SIZE = 2000; @@ -96,7 +96,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => >(async ({ services, params }) => { const { alertWithLifecycle, savedObjectsClient, scopedClusterClient } = services; const { sources } = libs; - const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, reason, threshold, value) => + const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, reason, value, threshold) => alertWithLifecycle({ id, fields: { @@ -251,8 +251,8 @@ export const processUngroupedResults = ( const alertInstance = alertInstanceFactory( UNGROUPED_FACTORY_KEY, getReasonMessageForUngroupedCountAlert(documentCount, count.value, count.comparator), - count.value, - documentCount + documentCount, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { @@ -285,8 +285,8 @@ export const processUngroupedRatioResults = ( const alertInstance = alertInstanceFactory( UNGROUPED_FACTORY_KEY, getReasonMessageForUngroupedRatioAlert(ratio, count.value, count.comparator), - count.value, - ratio + ratio, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { @@ -364,8 +364,8 @@ export const processGroupByResults = ( count.comparator, group.name ), - count.value, - documentCount + documentCount, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { @@ -415,8 +415,8 @@ export const processGroupByRatioResults = ( count.comparator, numeratorGroup.name ), - count.value, - ratio + ratio, + count.value ); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/counter_rate.test.ts b/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts similarity index 99% rename from x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/counter_rate.test.ts rename to x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts index 4b909489f3236..3e1a5ad8e3964 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/counter_rate.test.ts +++ b/x-pack/plugins/lens/common/expressions/counter_rate/counter_rate.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { counterRate, CounterRateArgs } from '../counter_rate'; +import { counterRate, CounterRateArgs } from './index'; import { Datatable } from 'src/plugins/expressions/public'; import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/index.ts b/x-pack/plugins/lens/common/expressions/counter_rate/index.ts similarity index 97% rename from x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/index.ts rename to x-pack/plugins/lens/common/expressions/counter_rate/index.ts index de59597ab6ba3..41f5547dff969 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/counter_rate/index.ts +++ b/x-pack/plugins/lens/common/expressions/counter_rate/index.ts @@ -6,11 +6,14 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public'; import { getBucketIdentifier, buildResultColumns, } from '../../../../../../src/plugins/expressions/common'; +import type { + ExpressionFunctionDefinition, + Datatable, +} from '../../../../../../src/plugins/expressions/common'; export interface CounterRateArgs { by?: string[]; diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts new file mode 100644 index 0000000000000..d2db63a01793e --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts @@ -0,0 +1,158 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { cloneDeep } from 'lodash'; +import type { + DatatableColumnMeta, + ExpressionFunctionDefinition, +} from '../../../../../../src/plugins/expressions/common'; +import type { FormatFactory, LensMultiTable } from '../../types'; +import type { ColumnConfigArg } from './datatable_column'; +import { getSortingCriteria } from './sorting'; +import { computeSummaryRowForColumn } from './summary'; +import { transposeTable } from './transpose_helpers'; + +export interface SortingState { + columnId: string | undefined; + direction: 'asc' | 'desc' | 'none'; +} + +export interface DatatableProps { + data: LensMultiTable; + untransposedData?: LensMultiTable; + args: DatatableArgs; +} + +export interface DatatableRender { + type: 'render'; + as: 'lens_datatable_renderer'; + value: DatatableProps; +} + +export interface DatatableArgs { + title: string; + description?: string; + columns: ColumnConfigArg[]; + sortingColumnId: SortingState['columnId']; + sortingDirection: SortingState['direction']; +} + +function isRange(meta: { params?: { id?: string } } | undefined) { + return meta?.params?.id === 'range'; +} + +export const getDatatable = ({ + formatFactory, +}: { + formatFactory: FormatFactory; +}): ExpressionFunctionDefinition< + 'lens_datatable', + LensMultiTable, + DatatableArgs, + DatatableRender +> => ({ + name: 'lens_datatable', + type: 'render', + inputTypes: ['lens_multitable'], + help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', { + defaultMessage: 'Datatable renderer', + }), + args: { + title: { + types: ['string'], + help: i18n.translate('xpack.lens.datatable.titleLabel', { + defaultMessage: 'Title', + }), + }, + description: { + types: ['string'], + help: '', + }, + columns: { + types: ['lens_datatable_column'], + help: '', + multi: true, + }, + sortingColumnId: { + types: ['string'], + help: '', + }, + sortingDirection: { + types: ['string'], + help: '', + }, + }, + fn(data, args, context) { + let untransposedData: LensMultiTable | undefined; + // do the sorting at this level to propagate it also at CSV download + const [firstTable] = Object.values(data.tables); + const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); + const formatters: Record> = {}; + + firstTable.columns.forEach((column) => { + formatters[column.id] = formatFactory(column.meta?.params); + }); + + const hasTransposedColumns = args.columns.some((c) => c.isTransposed); + if (hasTransposedColumns) { + // store original shape of data separately + untransposedData = cloneDeep(data); + // transposes table and args inplace + transposeTable(args, firstTable, formatters); + } + + const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args; + + const columnsReverseLookup = firstTable.columns.reduce< + Record + >((memo, { id, name, meta }, i) => { + memo[id] = { name, index: i, meta }; + return memo; + }, {}); + + const columnsWithSummary = args.columns.filter((c) => c.summaryRow); + for (const column of columnsWithSummary) { + column.summaryRowValue = computeSummaryRowForColumn( + column, + firstTable, + formatters, + formatFactory({ id: 'number' }) + ); + } + + if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') { + // Sort on raw values for these types, while use the formatted value for the rest + const sortingCriteria = getSortingCriteria( + isRange(columnsReverseLookup[sortBy]?.meta) + ? 'range' + : columnsReverseLookup[sortBy]?.meta?.type, + sortBy, + formatters[sortBy], + sortDirection + ); + // replace the table here + context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || []) + .slice() + .sort(sortingCriteria); + // replace also the local copy + firstTable.rows = context.inspectorAdapters.tables[layerId].rows; + } else { + args.sortingColumnId = undefined; + args.sortingDirection = 'none'; + } + return { + type: 'render', + as: 'lens_datatable_renderer', + value: { + data, + untransposedData, + args, + }, + }; + }, +}); diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts new file mode 100644 index 0000000000000..892631c3b0f45 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts @@ -0,0 +1,85 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Direction } from '@elastic/eui'; +import type { + CustomPaletteState, + PaletteOutput, +} from '../../../../../../src/plugins/charts/common'; +import type { + ExpressionFunctionDefinition, + DatatableColumn, +} from '../../../../../../src/plugins/expressions/common'; +import type { CustomPaletteParams } from '../../types'; + +export type LensGridDirection = 'none' | Direction; + +export interface ColumnConfig { + columns: ColumnConfigArg[]; + sortingColumnId: string | undefined; + sortingDirection: LensGridDirection; +} + +export type ColumnConfigArg = Omit & { + type: 'lens_datatable_column'; + palette?: PaletteOutput; + summaryRowValue?: unknown; +}; + +export interface ColumnState { + columnId: string; + width?: number; + hidden?: boolean; + isTransposed?: boolean; + // These flags are necessary to transpose columns and map them back later + // They are set automatically and are not user-editable + transposable?: boolean; + originalColumnId?: string; + originalName?: string; + bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>; + alignment?: 'left' | 'right' | 'center'; + palette?: PaletteOutput; + colorMode?: 'none' | 'cell' | 'text'; + summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max'; + summaryLabel?: string; +} + +export type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' }; + +export const datatableColumn: ExpressionFunctionDefinition< + 'lens_datatable_column', + null, + ColumnState, + DatatableColumnResult +> = { + name: 'lens_datatable_column', + aliases: [], + type: 'lens_datatable_column', + help: '', + inputTypes: ['null'], + args: { + columnId: { types: ['string'], help: '' }, + alignment: { types: ['string'], help: '' }, + hidden: { types: ['boolean'], help: '' }, + width: { types: ['number'], help: '' }, + isTransposed: { types: ['boolean'], help: '' }, + transposable: { types: ['boolean'], help: '' }, + colorMode: { types: ['string'], help: '' }, + palette: { + types: ['palette'], + help: '', + }, + summaryRow: { types: ['string'], help: '' }, + summaryLabel: { types: ['string'], help: '' }, + }, + fn: function fn(input: unknown, args: ColumnState) { + return { + type: 'lens_datatable_column', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/datatable/index.ts b/x-pack/plugins/lens/common/expressions/datatable/index.ts new file mode 100644 index 0000000000000..2602aae252ca9 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/datatable/index.ts @@ -0,0 +1,12 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './datatable_column'; +export * from './datatable'; +export * from './summary'; +export * from './transpose_helpers'; +export * from './utils'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/sorting.test.tsx b/x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/sorting.test.tsx rename to x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx diff --git a/x-pack/plugins/lens/public/datatable_visualization/sorting.tsx b/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/sorting.tsx rename to x-pack/plugins/lens/common/expressions/datatable/sorting.tsx index 0859ab5428c9e..13ca811b0b082 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/sorting.tsx +++ b/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx @@ -7,7 +7,7 @@ import ipaddr from 'ipaddr.js'; import type { IPv4, IPv6 } from 'ipaddr.js'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from '../../../../../../src/plugins/data/common'; function isIPv6Address(ip: IPv4 | IPv6): ip is IPv6 { return ip.kind() === 'ipv6'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/summary.test.ts b/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts similarity index 98% rename from x-pack/plugins/lens/public/datatable_visualization/summary.test.ts rename to x-pack/plugins/lens/common/expressions/datatable/summary.test.ts index f92c83fbbfdc8..9f8f56cc92768 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/summary.test.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IFieldFormat } from 'src/plugins/data/public'; +import { IFieldFormat } from 'src/plugins/data/common'; import { Datatable } from 'src/plugins/expressions'; import { computeSummaryRowForColumn, getFinalSummaryConfiguration } from './summary'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/summary.ts b/x-pack/plugins/lens/common/expressions/datatable/summary.ts similarity index 91% rename from x-pack/plugins/lens/public/datatable_visualization/summary.ts rename to x-pack/plugins/lens/common/expressions/datatable/summary.ts index 6c267445aab76..aceade2a3a513 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/summary.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/summary.ts @@ -6,11 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { FieldFormat } from 'src/plugins/data/public'; -import { Datatable } from 'src/plugins/expressions/public'; -import { ColumnConfigArg } from './datatable_visualization'; +import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { Datatable } from '../../../../../../src/plugins/expressions/common'; +import { ColumnConfigArg } from './datatable_column'; import { getOriginalId } from './transpose_helpers'; -import { isNumericField } from './utils'; +import { isNumericFieldForDatatable } from './utils'; type SummaryRowType = Extract; @@ -19,7 +19,7 @@ export function getFinalSummaryConfiguration( columnArgs: Pick | undefined, table: Datatable | undefined ) { - const isNumeric = isNumericField(table, columnId); + const isNumeric = isNumericFieldForDatatable(table, columnId); const summaryRow = isNumeric ? columnArgs?.summaryRow || 'none' : 'none'; const summaryLabel = columnArgs?.summaryLabel ?? getDefaultSummaryLabel(summaryRow); diff --git a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts similarity index 99% rename from x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts rename to x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts index 91559a1778f4f..7ac6b3d987c84 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.test.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts @@ -7,8 +7,8 @@ import type { FieldFormat } from 'src/plugins/data/public'; import type { Datatable } from 'src/plugins/expressions'; +import { DatatableArgs } from './datatable'; -import { Args } from './expression'; import { transposeTable } from './transpose_helpers'; describe('transpose_helpes', () => { @@ -59,7 +59,7 @@ describe('transpose_helpes', () => { }; } - function buildArgs(): Args { + function buildArgs(): DatatableArgs { return { title: 'Table', sortingColumnId: undefined, diff --git a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts similarity index 95% rename from x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts rename to x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts index a35edf7499073..06798413c8f40 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/transpose_helpers.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts @@ -5,11 +5,14 @@ * 2.0. */ -import type { FieldFormat } from 'src/plugins/data/public'; -import type { Datatable, DatatableColumn, DatatableRow } from 'src/plugins/expressions'; -import { ColumnConfig } from './components/table_basic'; - -import { Args, ColumnConfigArg } from './expression'; +import type { + Datatable, + DatatableColumn, + DatatableRow, +} from '../../../../../../src/plugins/expressions'; +import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { DatatableArgs } from './datatable'; +import type { ColumnConfig, ColumnConfigArg } from './datatable_column'; const TRANSPOSE_SEPARATOR = '---'; @@ -42,7 +45,7 @@ export function getOriginalId(id: string) { * @param formatters Formatters for all columns to transpose columns by actual display values */ export function transposeTable( - args: Args, + args: DatatableArgs, firstTable: Datatable, formatters: Record ) { @@ -112,7 +115,7 @@ function transposeRows( * grouped by unique value */ function updateColumnArgs( - args: Args, + args: DatatableArgs, bucketsColumnArgs: ColumnConfig['columns'], transposedColumnGroups: Array ) { @@ -150,7 +153,7 @@ function getUniqueValues(table: Datatable, formatter: FieldFormat, columnId: str * @param uniqueValues */ function transposeColumns( - args: Args, + args: DatatableArgs, bucketsColumnArgs: ColumnConfig['columns'], metricColumns: ColumnConfig['columns'], firstTable: Datatable, diff --git a/x-pack/plugins/lens/public/datatable_visualization/utils.ts b/x-pack/plugins/lens/common/expressions/datatable/utils.ts similarity index 80% rename from x-pack/plugins/lens/public/datatable_visualization/utils.ts rename to x-pack/plugins/lens/common/expressions/datatable/utils.ts index 64fdee233e830..486ec7102e1eb 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/utils.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/utils.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { Datatable } from 'src/plugins/expressions/public'; +import type { Datatable } from '../../../../../../src/plugins/expressions/common'; import { getOriginalId } from './transpose_helpers'; function isValidNumber(value: unknown): boolean { return typeof value === 'number' || value == null; } -export function isNumericField(currentData: Datatable | undefined, accessor: string) { +export function isNumericFieldForDatatable(currentData: Datatable | undefined, accessor: string) { const isNumeric = currentData?.columns.find((col) => col.id === accessor || getOriginalId(col.id) === accessor) ?.meta.type === 'number'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.test.ts b/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts similarity index 98% rename from x-pack/plugins/lens/public/indexpattern_datasource/format_column.test.ts rename to x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts index 38643f2dfbda7..4428225b349da 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.test.ts +++ b/x-pack/plugins/lens/common/expressions/format_column/format_column.test.ts @@ -7,7 +7,7 @@ import { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils'; -import { FormatColumnArgs, formatColumn } from './format_column'; +import { FormatColumnArgs, formatColumn } from './index'; describe('format_column', () => { const fn: (input: Datatable, args: FormatColumnArgs) => Datatable = functionWrapper(formatColumn); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts b/x-pack/plugins/lens/common/expressions/format_column/index.ts similarity index 98% rename from x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts rename to x-pack/plugins/lens/common/expressions/format_column/index.ts index 09a4e607a1586..c874eac1ede1f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts +++ b/x-pack/plugins/lens/common/expressions/format_column/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { +import type { ExpressionFunctionDefinition, Datatable, DatatableColumn, -} from 'src/plugins/expressions/public'; +} from '../../../../../../src/plugins/expressions/common'; export interface FormatColumnArgs { format: string; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_chart.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_chart.ts new file mode 100644 index 0000000000000..4674879dcac9f --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_chart.ts @@ -0,0 +1,122 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; +import type { LensMultiTable, CustomPaletteParams } from '../../types'; +import { HeatmapGridConfigResult, HEATMAP_GRID_FUNCTION } from './heatmap_grid'; +import { HeatmapLegendConfigResult, HEATMAP_LEGEND_FUNCTION } from './heatmap_legend'; + +export const HEATMAP_FUNCTION = 'lens_heatmap'; +export const HEATMAP_FUNCTION_RENDERER = 'lens_heatmap_renderer'; + +export type ChartShapes = 'heatmap'; + +export interface SharedHeatmapLayerState { + shape: ChartShapes; + xAccessor?: string; + yAccessor?: string; + valueAccessor?: string; + legend: HeatmapLegendConfigResult; + gridConfig: HeatmapGridConfigResult; +} + +export type HeatmapLayerState = SharedHeatmapLayerState & { + layerId: string; +}; + +export type HeatmapVisualizationState = HeatmapLayerState & { + // need to store the current accessor to reset the color stops at accessor change + palette?: PaletteOutput & { accessor: string }; +}; + +export type HeatmapExpressionArgs = SharedHeatmapLayerState & { + title?: string; + description?: string; + palette: PaletteOutput; +}; + +export interface HeatmapRender { + type: 'render'; + as: typeof HEATMAP_FUNCTION_RENDERER; + value: HeatmapExpressionProps; +} + +export interface HeatmapExpressionProps { + data: LensMultiTable; + args: HeatmapExpressionArgs; +} + +export const heatmap: ExpressionFunctionDefinition< + typeof HEATMAP_FUNCTION, + LensMultiTable, + HeatmapExpressionArgs, + HeatmapRender +> = { + name: HEATMAP_FUNCTION, + type: 'render', + help: i18n.translate('xpack.lens.heatmap.expressionHelpLabel', { + defaultMessage: 'Heatmap renderer', + }), + args: { + title: { + types: ['string'], + help: i18n.translate('xpack.lens.heatmap.titleLabel', { + defaultMessage: 'Title', + }), + }, + description: { + types: ['string'], + help: '', + }, + xAccessor: { + types: ['string'], + help: '', + }, + yAccessor: { + types: ['string'], + help: '', + }, + valueAccessor: { + types: ['string'], + help: '', + }, + shape: { + types: ['string'], + help: '', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + legend: { + types: [HEATMAP_LEGEND_FUNCTION], + help: i18n.translate('xpack.lens.heatmapChart.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + gridConfig: { + types: [HEATMAP_GRID_FUNCTION], + help: i18n.translate('xpack.lens.heatmapChart.gridConfig.help', { + defaultMessage: 'Configure the heatmap layout.', + }), + }, + }, + inputTypes: ['lens_multitable'], + fn(data: LensMultiTable, args: HeatmapExpressionArgs) { + return { + type: 'render', + as: HEATMAP_FUNCTION_RENDERER, + value: { + data, + args, + }, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_grid.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_grid.ts new file mode 100644 index 0000000000000..5fe7f4b8f6c62 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_grid.ts @@ -0,0 +1,114 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export const HEATMAP_GRID_FUNCTION = 'lens_heatmap_grid'; + +export interface HeatmapGridConfig { + // grid + strokeWidth?: number; + strokeColor?: string; + cellHeight?: number; + cellWidth?: number; + // cells + isCellLabelVisible: boolean; + // Y-axis + isYAxisLabelVisible: boolean; + yAxisLabelWidth?: number; + yAxisLabelColor?: string; + // X-axis + isXAxisLabelVisible: boolean; +} + +export type HeatmapGridConfigResult = HeatmapGridConfig & { type: typeof HEATMAP_GRID_FUNCTION }; + +export const heatmapGridConfig: ExpressionFunctionDefinition< + typeof HEATMAP_GRID_FUNCTION, + null, + HeatmapGridConfig, + HeatmapGridConfigResult +> = { + name: HEATMAP_GRID_FUNCTION, + aliases: [], + type: HEATMAP_GRID_FUNCTION, + help: `Configure the heatmap layout `, + inputTypes: ['null'], + args: { + // grid + strokeWidth: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.strokeWidth.help', { + defaultMessage: 'Specifies the grid stroke width', + }), + required: false, + }, + strokeColor: { + types: ['string'], + help: i18n.translate('xpack.lens.heatmapChart.config.strokeColor.help', { + defaultMessage: 'Specifies the grid stroke color', + }), + required: false, + }, + cellHeight: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.cellHeight.help', { + defaultMessage: 'Specifies the grid cell height', + }), + required: false, + }, + cellWidth: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.cellWidth.help', { + defaultMessage: 'Specifies the grid cell width', + }), + required: false, + }, + // cells + isCellLabelVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.config.isCellLabelVisible.help', { + defaultMessage: 'Specifies whether or not the cell label is visible.', + }), + }, + // Y-axis + isYAxisLabelVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.config.isYAxisLabelVisible.help', { + defaultMessage: 'Specifies whether or not the Y-axis labels are visible.', + }), + }, + yAxisLabelWidth: { + types: ['number'], + help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelWidth.help', { + defaultMessage: 'Specifies the width of the Y-axis labels.', + }), + required: false, + }, + yAxisLabelColor: { + types: ['string'], + help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelColor.help', { + defaultMessage: 'Specifies the color of the Y-axis labels.', + }), + required: false, + }, + // X-axis + isXAxisLabelVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.config.isXAxisLabelVisible.help', { + defaultMessage: 'Specifies whether or not the X-axis labels are visible.', + }), + }, + }, + fn(input, args) { + return { + type: HEATMAP_GRID_FUNCTION, + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_legend.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_legend.ts new file mode 100644 index 0000000000000..0f553c6cae1f0 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/heatmap_legend.ts @@ -0,0 +1,64 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Position } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export const HEATMAP_LEGEND_FUNCTION = 'lens_heatmap_legendConfig'; + +export interface HeatmapLegendConfig { + /** + * Flag whether the legend should be shown. If there is just a single series, it will be hidden + */ + isVisible: boolean; + /** + * Position of the legend relative to the chart + */ + position: Position; +} + +export type HeatmapLegendConfigResult = HeatmapLegendConfig & { + type: typeof HEATMAP_LEGEND_FUNCTION; +}; + +/** + * TODO check if it's possible to make a shared function + * based on the XY chart + */ +export const heatmapLegendConfig: ExpressionFunctionDefinition< + typeof HEATMAP_LEGEND_FUNCTION, + null, + HeatmapLegendConfig, + HeatmapLegendConfigResult +> = { + name: HEATMAP_LEGEND_FUNCTION, + aliases: [], + type: HEATMAP_LEGEND_FUNCTION, + help: `Configure the heatmap chart's legend`, + inputTypes: ['null'], + args: { + isVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.heatmapChart.legend.isVisible.help', { + defaultMessage: 'Specifies whether or not the legend is visible.', + }), + }, + position: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('xpack.lens.heatmapChart.legend.position.help', { + defaultMessage: 'Specifies the legend position.', + }), + }, + }, + fn(input, args) { + return { + type: HEATMAP_LEGEND_FUNCTION, + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/heatmap_chart/index.ts b/x-pack/plugins/lens/common/expressions/heatmap_chart/index.ts new file mode 100644 index 0000000000000..96f8c074b1fc4 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/heatmap_chart/index.ts @@ -0,0 +1,10 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './heatmap_grid'; +export * from './heatmap_legend'; +export * from './heatmap_chart'; diff --git a/x-pack/plugins/lens/common/expressions/index.ts b/x-pack/plugins/lens/common/expressions/index.ts new file mode 100644 index 0000000000000..70a85f85938e4 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/index.ts @@ -0,0 +1,17 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './counter_rate'; +export * from './format_column'; +export * from './rename_columns'; +export * from './merge_tables'; +export * from './time_scale'; +export * from './datatable'; +export * from './heatmap_chart'; +export * from './metric_chart'; +export * from './pie_chart'; +export * from './xy_chart'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts b/x-pack/plugins/lens/common/expressions/merge_tables/index.ts similarity index 84% rename from x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts rename to x-pack/plugins/lens/common/expressions/merge_tables/index.ts index cd93392fc712d..e190da19886df 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts +++ b/x-pack/plugins/lens/common/expressions/merge_tables/index.ts @@ -6,16 +6,16 @@ */ import { i18n } from '@kbn/i18n'; -import { - ExecutionContext, - Datatable, +import type { ExpressionFunctionDefinition, -} from 'src/plugins/expressions/public'; -import { ExpressionValueSearchContext, search } from '../../../../../src/plugins/data/public'; -const { toAbsoluteDates } = search.aggs; + Datatable, + ExecutionContext, +} from '../../../../../../src/plugins/expressions/common'; +import { toAbsoluteDates } from '../../../../../../src/plugins/data/common'; +import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common'; -import { LensMultiTable } from '../types'; -import { Adapters } from '../../../../../src/plugins/inspector/common'; +import type { LensMultiTable } from '../../types'; +import { Adapters } from '../../../../../../src/plugins/inspector/common'; interface MergeTables { layerIds: string[]; diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts similarity index 98% rename from x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts rename to x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts index eb381b33655e2..c883f6b7cb479 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts +++ b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import { mergeTables } from './merge_tables'; +import { mergeTables } from './index'; import { ExpressionValueSearchContext } from 'src/plugins/data/public'; import { Datatable, diff --git a/x-pack/plugins/lens/common/expressions/metric_chart/index.ts b/x-pack/plugins/lens/common/expressions/metric_chart/index.ts new file mode 100644 index 0000000000000..40bd4f3886455 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/metric_chart/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './types'; +export * from './metric_chart'; diff --git a/x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts b/x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts new file mode 100644 index 0000000000000..53ed7c8da32eb --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts @@ -0,0 +1,68 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { LensMultiTable } from '../../types'; +import type { MetricConfig } from './types'; + +export interface MetricChartProps { + data: LensMultiTable; + args: MetricConfig; +} + +export interface MetricRender { + type: 'render'; + as: 'lens_metric_chart_renderer'; + value: MetricChartProps; +} + +export const metricChart: ExpressionFunctionDefinition< + 'lens_metric_chart', + LensMultiTable, + Omit, + MetricRender +> = { + name: 'lens_metric_chart', + type: 'render', + help: 'A metric chart', + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + metricTitle: { + types: ['string'], + help: 'The title of the metric shown.', + }, + accessor: { + types: ['string'], + help: 'The column whose value is being displayed', + }, + mode: { + types: ['string'], + options: ['reduced', 'full'], + default: 'full', + help: + 'The display mode of the chart - reduced will only show the metric itself without min size', + }, + }, + inputTypes: ['lens_multitable'], + fn(data, args) { + return { + type: 'render', + as: 'lens_metric_chart_renderer', + value: { + data, + args, + }, + } as MetricRender; + }, +}; diff --git a/x-pack/plugins/lens/public/metric_visualization/types.ts b/x-pack/plugins/lens/common/expressions/metric_chart/types.ts similarity index 100% rename from x-pack/plugins/lens/public/metric_visualization/types.ts rename to x-pack/plugins/lens/common/expressions/metric_chart/types.ts diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/index.ts b/x-pack/plugins/lens/common/expressions/pie_chart/index.ts new file mode 100644 index 0000000000000..e82294f8aff25 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/pie_chart/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './types'; +export * from './pie_chart'; diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts new file mode 100644 index 0000000000000..b298f1d8b3a80 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts @@ -0,0 +1,103 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Position } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { LensMultiTable } from '../../types'; +import type { PieExpressionProps, PieExpressionArgs } from './types'; + +export interface PieRender { + type: 'render'; + as: 'lens_pie_renderer'; + value: PieExpressionProps; +} + +export const pie: ExpressionFunctionDefinition< + 'lens_pie', + LensMultiTable, + PieExpressionArgs, + PieRender +> = { + name: 'lens_pie', + type: 'render', + help: i18n.translate('xpack.lens.pie.expressionHelpLabel', { + defaultMessage: 'Pie renderer', + }), + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + groups: { + types: ['string'], + multi: true, + help: '', + }, + metric: { + types: ['string'], + help: '', + }, + shape: { + types: ['string'], + options: ['pie', 'donut', 'treemap'], + help: '', + }, + hideLabels: { + types: ['boolean'], + help: '', + }, + numberDisplay: { + types: ['string'], + options: ['hidden', 'percent', 'value'], + help: '', + }, + categoryDisplay: { + types: ['string'], + options: ['default', 'inside', 'hide'], + help: '', + }, + legendDisplay: { + types: ['string'], + options: ['default', 'show', 'hide'], + help: '', + }, + nestedLegend: { + types: ['boolean'], + help: '', + }, + legendPosition: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: '', + }, + percentDecimals: { + types: ['number'], + help: '', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + }, + inputTypes: ['lens_multitable'], + fn(data: LensMultiTable, args: PieExpressionArgs) { + return { + type: 'render', + as: 'lens_pie_renderer', + value: { + data, + args, + }, + }; + }, +}; diff --git a/x-pack/plugins/lens/public/pie_visualization/types.ts b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts similarity index 89% rename from x-pack/plugins/lens/public/pie_visualization/types.ts rename to x-pack/plugins/lens/common/expressions/pie_chart/types.ts index c03ab15ecc290..e377272322950 100644 --- a/x-pack/plugins/lens/public/pie_visualization/types.ts +++ b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { PaletteOutput } from 'src/plugins/charts/public'; -import { LensMultiTable } from '../types'; +import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; +import type { LensMultiTable } from '../../types'; export interface SharedPieLayerState { groups: string[]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/index.ts b/x-pack/plugins/lens/common/expressions/rename_columns/index.ts similarity index 87% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/index.ts rename to x-pack/plugins/lens/common/expressions/rename_columns/index.ts index a69e0358d69c6..4cb8ff75f486d 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/metric/component/index.ts +++ b/x-pack/plugins/lens/common/expressions/rename_columns/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { Metric } from './metric'; +export * from './rename_columns'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.test.ts similarity index 96% rename from x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts rename to x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.test.ts index 5654a599c5e27..f3db64c1d2257 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts +++ b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.test.ts @@ -6,8 +6,8 @@ */ import { renameColumns } from './rename_columns'; -import { Datatable } from '../../../../../src/plugins/expressions/public'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; +import { Datatable } from '../../../../../../src/plugins/expressions/common'; +import { createMockExecutionContext } from '../../../../../../src/plugins/expressions/common/mocks'; describe('rename_columns', () => { it('should rename columns of a given datatable', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.ts similarity index 86% rename from x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts rename to x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.ts index a16756126c030..517bd683d80ae 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts +++ b/x-pack/plugins/lens/common/expressions/rename_columns/rename_columns.ts @@ -6,14 +6,20 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable, DatatableColumn } from 'src/plugins/expressions'; -import { IndexPatternColumn } from './operations'; +import { + ExpressionFunctionDefinition, + Datatable, + DatatableColumn, +} from '../../../../../../src/plugins/expressions/common'; interface RemapArgs { idMap: string; } -export type OriginalColumn = { id: string } & IndexPatternColumn; +type OriginalColumn = { id: string; label: string } & ( + | { operationType: 'date_histogram'; sourceField: string } + | { operationType: string; sourceField: never } +); export const renameColumns: ExpressionFunctionDefinition< 'lens_rename_columns', @@ -75,7 +81,7 @@ export const renameColumns: ExpressionFunctionDefinition< }; function getColumnName(originalColumn: OriginalColumn, newColumn: DatatableColumn) { - if (originalColumn && originalColumn.operationType === 'date_histogram') { + if (originalColumn?.operationType === 'date_histogram') { const fieldName = originalColumn.sourceField; // HACK: This is a hack, and introduces some fragility into diff --git a/x-pack/plugins/lens/common/expressions/time_scale/index.ts b/x-pack/plugins/lens/common/expressions/time_scale/index.ts new file mode 100644 index 0000000000000..92fec01a9ecbc --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/time_scale/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './time_scale'; +export * from './types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.test.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts similarity index 89% rename from x-pack/plugins/lens/public/indexpattern_datasource/time_scale.test.ts rename to x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts index 34579927cfe19..c0a5c4bf1e1ec 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.test.ts +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.test.ts @@ -7,14 +7,25 @@ import moment from 'moment'; import { Datatable } from 'src/plugins/expressions/public'; -import { DataPublicPluginStart, TimeRange } from 'src/plugins/data/public'; -import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; +import { TimeRange } from 'src/plugins/data/public'; import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils'; -import { getTimeScaleFunction, TimeScaleArgs } from './time_scale'; + +// mock the specific inner variable: +// there are intra dependencies in the data plugin we might break trying to mock the whole thing +jest.mock('../../../../../../src/plugins/data/common/query/timefilter/get_time', () => { + const localMoment = jest.requireActual('moment'); + return { + calculateBounds: jest.fn(({ from, to }) => ({ + min: localMoment(from), + max: localMoment(to), + })), + }; +}); + +import { timeScale, TimeScaleArgs } from './time_scale'; describe('time_scale', () => { - let timeScale: (input: Datatable, args: TimeScaleArgs) => Promise; - let dataMock: jest.Mocked; + let timeScaleWrapped: (input: Datatable, args: TimeScaleArgs) => Promise; const emptyTable: Datatable = { type: 'datatable', @@ -61,7 +72,6 @@ describe('time_scale', () => { } beforeEach(() => { - dataMock = dataPluginMock.createStartContract(); setDateHistogramMeta({ timeZone: 'UTC', timeRange: { @@ -70,17 +80,11 @@ describe('time_scale', () => { }, interval: '1d', }); - (dataMock.query.timefilter.timefilter.calculateBounds as jest.Mock).mockImplementation( - ({ from, to }) => ({ - min: moment(from), - max: moment(to), - }) - ); - timeScale = functionWrapper(getTimeScaleFunction(dataMock)); + timeScaleWrapped = functionWrapper(timeScale); }); it('should apply time scale factor to each row', async () => { - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -115,7 +119,7 @@ describe('time_scale', () => { }); it('should skip gaps in the data', async () => { - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -163,7 +167,7 @@ describe('time_scale', () => { }, ], }; - const result = await timeScale(mismatchedTable, { + const result = await timeScaleWrapped(mismatchedTable, { ...defaultArgs, inputColumnId: 'nonexistent', }); @@ -180,7 +184,7 @@ describe('time_scale', () => { }, interval: '1h', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -220,7 +224,7 @@ describe('time_scale', () => { }, interval: '3h', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -262,7 +266,7 @@ describe('time_scale', () => { }, interval: '1d', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -307,7 +311,7 @@ describe('time_scale', () => { }, interval: '1d', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ @@ -347,7 +351,7 @@ describe('time_scale', () => { }, interval: '1y', }); - const result = await timeScale( + const result = await timeScaleWrapped( { ...emptyTable, rows: [ diff --git a/x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts new file mode 100644 index 0000000000000..fc2023ca4d599 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts @@ -0,0 +1,151 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment-timezone'; +import { i18n } from '@kbn/i18n'; +import type { + ExpressionFunctionDefinition, + Datatable, +} from '../../../../../../src/plugins/expressions/common'; +import { + getDateHistogramMetaDataByDatatableColumn, + parseInterval, + calculateBounds, +} from '../../../../../../src/plugins/data/common'; +import { buildResultColumns } from '../../../../../../src/plugins/expressions/common'; +import type { TimeScaleUnit } from './types'; + +export interface TimeScaleArgs { + dateColumnId: string; + inputColumnId: string; + outputColumnId: string; + targetUnit: TimeScaleUnit; + outputColumnName?: string; +} + +const unitInMs: Record = { + s: 1000, + m: 1000 * 60, + h: 1000 * 60 * 60, + d: 1000 * 60 * 60 * 24, +}; + +export const timeScale: ExpressionFunctionDefinition< + 'lens_time_scale', + Datatable, + TimeScaleArgs, + Promise +> = { + name: 'lens_time_scale', + type: 'datatable', + help: '', + args: { + dateColumnId: { + types: ['string'], + help: '', + required: true, + }, + inputColumnId: { + types: ['string'], + help: '', + required: true, + }, + outputColumnId: { + types: ['string'], + help: '', + required: true, + }, + outputColumnName: { + types: ['string'], + help: '', + }, + targetUnit: { + types: ['string'], + options: ['s', 'm', 'h', 'd'], + help: '', + required: true, + }, + }, + inputTypes: ['datatable'], + async fn( + input, + { dateColumnId, inputColumnId, outputColumnId, outputColumnName, targetUnit }: TimeScaleArgs + ) { + const dateColumnDefinition = input.columns.find((column) => column.id === dateColumnId); + + if (!dateColumnDefinition) { + throw new Error( + i18n.translate('xpack.lens.functions.timeScale.dateColumnMissingMessage', { + defaultMessage: 'Specified dateColumnId {columnId} does not exist.', + values: { + columnId: dateColumnId, + }, + }) + ); + } + + const resultColumns = buildResultColumns( + input, + outputColumnId, + inputColumnId, + outputColumnName, + { allowColumnOverwrite: true } + ); + + if (!resultColumns) { + return input; + } + + const targetUnitInMs = unitInMs[targetUnit]; + const timeInfo = getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition); + const intervalDuration = timeInfo?.interval && parseInterval(timeInfo.interval); + + if (!timeInfo || !intervalDuration) { + throw new Error( + i18n.translate('xpack.lens.functions.timeScale.timeInfoMissingMessage', { + defaultMessage: 'Could not fetch date histogram information', + }) + ); + } + // the datemath plugin always parses dates by using the current default moment time zone. + // to use the configured time zone, we are switching just for the bounds calculation. + const defaultTimezone = moment().zoneName(); + moment.tz.setDefault(timeInfo.timeZone); + + const timeBounds = timeInfo.timeRange && calculateBounds(timeInfo.timeRange); + + const result = { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + + let startOfBucket = moment(row[dateColumnId]); + let endOfBucket = startOfBucket.clone().add(intervalDuration); + if (timeBounds && timeBounds.min) { + startOfBucket = moment.max(startOfBucket, timeBounds.min); + } + if (timeBounds && timeBounds.max) { + endOfBucket = moment.min(endOfBucket, timeBounds.max); + } + const bucketSize = endOfBucket.diff(startOfBucket); + const factor = bucketSize / targetUnitInMs; + + const currentValue = newRow[inputColumnId]; + if (currentValue != null) { + newRow[outputColumnId] = Number(currentValue) / factor; + } + + return newRow; + }), + }; + // reset default moment timezone + moment.tz.setDefault(defaultTimezone); + + return result; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/time_scale/types.ts b/x-pack/plugins/lens/common/expressions/time_scale/types.ts new file mode 100644 index 0000000000000..4ee00ce53e68b --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/time_scale/types.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type TimeScaleUnit = 's' | 'm' | 'h' | 'd'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts new file mode 100644 index 0000000000000..9a9273e43f6f1 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts @@ -0,0 +1,171 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import type { + ArgumentType, + ExpressionFunctionDefinition, +} from '../../../../../../src/plugins/expressions/common'; + +export interface AxesSettingsConfig { + x: boolean; + yLeft: boolean; + yRight: boolean; +} + +export interface AxisExtentConfig { + mode: 'full' | 'dataBounds' | 'custom'; + lowerBound?: number; + upperBound?: number; +} + +interface AxisConfig { + title: string; + hide?: boolean; +} + +export type YAxisMode = 'auto' | 'left' | 'right'; + +export interface YConfig { + forAccessor: string; + axisMode?: YAxisMode; + color?: string; +} + +export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { + type: 'lens_xy_axisTitlesVisibilityConfig'; +}; + +export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition< + 'lens_xy_axisTitlesVisibilityConfig', + null, + AxesSettingsConfig, + AxisTitlesVisibilityConfigResult +> = { + name: 'lens_xy_axisTitlesVisibilityConfig', + aliases: [], + type: 'lens_xy_axisTitlesVisibilityConfig', + help: `Configure the xy chart's axis titles appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_axisTitlesVisibilityConfig', + ...args, + }; + }, +}; + +export type AxisExtentConfigResult = AxisExtentConfig & { type: 'lens_xy_axisExtentConfig' }; + +export const axisExtentConfig: ExpressionFunctionDefinition< + 'lens_xy_axisExtentConfig', + null, + AxisExtentConfig, + AxisExtentConfigResult +> = { + name: 'lens_xy_axisExtentConfig', + aliases: [], + type: 'lens_xy_axisExtentConfig', + help: `Configure the xy chart's axis extents`, + inputTypes: ['null'], + args: { + mode: { + types: ['string'], + options: ['full', 'dataBounds', 'custom'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + lowerBound: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + upperBound: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + }, + fn: function fn(input: unknown, args: AxisExtentConfig) { + return { + type: 'lens_xy_axisExtentConfig', + ...args, + }; + }, +}; + +export const axisConfig: { [key in keyof AxisConfig]: ArgumentType } = { + title: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.title.help', { + defaultMessage: 'The axis title', + }), + }, + hide: { + types: ['boolean'], + default: false, + help: 'Show / hide axis', + }, +}; + +export type YConfigResult = YConfig & { type: 'lens_xy_yConfig' }; + +export const yAxisConfig: ExpressionFunctionDefinition< + 'lens_xy_yConfig', + null, + YConfig, + YConfigResult +> = { + name: 'lens_xy_yConfig', + aliases: [], + type: 'lens_xy_yConfig', + help: `Configure the behavior of a xy chart's y axis metric`, + inputTypes: ['null'], + args: { + forAccessor: { + types: ['string'], + help: 'The accessor this configuration is for', + }, + axisMode: { + types: ['string'], + options: ['auto', 'left', 'right'], + help: 'The axis mode of the metric', + }, + color: { + types: ['string'], + help: 'The color of the series', + }, + }, + fn: function fn(input: unknown, args: YConfig) { + return { + type: 'lens_xy_yConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts b/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts new file mode 100644 index 0000000000000..0cfea62d578d7 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts @@ -0,0 +1,58 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export type FittingFunction = typeof fittingFunctionDefinitions[number]['id']; + +export const fittingFunctionDefinitions = [ + { + id: 'None', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { + defaultMessage: 'Hide', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', { + defaultMessage: 'Do not fill gaps', + }), + }, + { + id: 'Zero', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { + defaultMessage: 'Zero', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', { + defaultMessage: 'Fill gaps with zeros', + }), + }, + { + id: 'Linear', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { + defaultMessage: 'Linear', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', { + defaultMessage: 'Fill gaps with a line', + }), + }, + { + id: 'Carry', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { + defaultMessage: 'Last', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', { + defaultMessage: 'Fill gaps with the last value', + }), + }, + { + id: 'Lookahead', + title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { + defaultMessage: 'Next', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', { + defaultMessage: 'Fill gaps with the next value', + }), + }, +] as const; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts new file mode 100644 index 0000000000000..6338e9f039937 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts @@ -0,0 +1,51 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { AxesSettingsConfig } from './axis_config'; + +export type GridlinesConfigResult = AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; + +export const gridlinesConfig: ExpressionFunctionDefinition< + 'lens_xy_gridlinesConfig', + null, + AxesSettingsConfig, + GridlinesConfigResult +> = { + name: 'lens_xy_gridlinesConfig', + aliases: [], + type: 'lens_xy_gridlinesConfig', + help: `Configure the xy chart's gridlines appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_gridlinesConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/index.ts b/x-pack/plugins/lens/common/expressions/xy_chart/index.ts new file mode 100644 index 0000000000000..4d1125fa459a3 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/index.ts @@ -0,0 +1,16 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './axis_config'; +export * from './fitting_function'; +export * from './grid_lines_config'; +export * from './layer_config'; +export * from './legend_config'; +export * from './series_type'; +export * from './tick_labels_config'; +export * from './xy_args'; +export * from './xy_chart'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts new file mode 100644 index 0000000000000..f3baf242425f5 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts @@ -0,0 +1,120 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { axisConfig, YConfig } from './axis_config'; +import type { SeriesType } from './series_type'; + +export interface XYLayerConfig { + hide?: boolean; + layerId: string; + xAccessor?: string; + accessors: string[]; + yConfig?: YConfig[]; + seriesType: SeriesType; + splitAccessor?: string; + palette?: PaletteOutput; +} + +export interface ValidLayer extends XYLayerConfig { + xAccessor: NonNullable; +} + +export type LayerArgs = XYLayerConfig & { + columnToLabel?: string; // Actually a JSON key-value pair + yScaleType: 'time' | 'linear' | 'log' | 'sqrt'; + xScaleType: 'time' | 'linear' | 'ordinal'; + isHistogram: boolean; + // palette will always be set on the expression + palette: PaletteOutput; +}; + +export type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' }; + +export const layerConfig: ExpressionFunctionDefinition< + 'lens_xy_layer', + null, + LayerArgs, + LayerConfigResult +> = { + name: 'lens_xy_layer', + aliases: [], + type: 'lens_xy_layer', + help: `Configure a layer in the xy chart`, + inputTypes: ['null'], + args: { + ...axisConfig, + layerId: { + types: ['string'], + help: '', + }, + xAccessor: { + types: ['string'], + help: '', + }, + seriesType: { + types: ['string'], + options: [ + 'bar', + 'line', + 'area', + 'bar_stacked', + 'area_stacked', + 'bar_percentage_stacked', + 'area_percentage_stacked', + ], + help: 'The type of chart to display.', + }, + xScaleType: { + options: ['ordinal', 'linear', 'time'], + help: 'The scale type of the x axis', + default: 'ordinal', + }, + isHistogram: { + types: ['boolean'], + default: false, + help: 'Whether to layout the chart as a histogram', + }, + yScaleType: { + options: ['log', 'sqrt', 'linear', 'time'], + help: 'The scale type of the y axes', + default: 'linear', + }, + splitAccessor: { + types: ['string'], + help: 'The column to split by', + multi: false, + }, + accessors: { + types: ['string'], + help: 'The columns to display on the y axis.', + multi: true, + }, + yConfig: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + types: ['lens_xy_yConfig' as any], + help: 'Additional configuration for y axes', + multi: true, + }, + columnToLabel: { + types: ['string'], + help: 'JSON key-value pairs of column ID to label', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + }, + fn: function fn(input: unknown, args: LayerArgs) { + return { + type: 'lens_xy_layer', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts new file mode 100644 index 0000000000000..e228039b53ef6 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts @@ -0,0 +1,110 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; + +export interface LegendConfig { + /** + * Flag whether the legend should be shown. If there is just a single series, it will be hidden + */ + isVisible: boolean; + /** + * Position of the legend relative to the chart + */ + position: Position; + /** + * Flag whether the legend should be shown even with just a single series + */ + showSingleSeries?: boolean; + /** + * Flag whether the legend is inside the chart + */ + isInside?: boolean; + /** + * Horizontal Alignment of the legend when it is set inside chart + */ + horizontalAlignment?: HorizontalAlignment; + /** + * Vertical Alignment of the legend when it is set inside chart + */ + verticalAlignment?: VerticalAlignment; + /** + * Number of columns when legend is set inside chart + */ + floatingColumns?: number; +} + +export type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' }; + +export const legendConfig: ExpressionFunctionDefinition< + 'lens_xy_legendConfig', + null, + LegendConfig, + LegendConfigResult +> = { + name: 'lens_xy_legendConfig', + aliases: [], + type: 'lens_xy_legendConfig', + help: `Configure the xy chart's legend`, + inputTypes: ['null'], + args: { + isVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.isVisible.help', { + defaultMessage: 'Specifies whether or not the legend is visible.', + }), + }, + position: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('xpack.lens.xyChart.position.help', { + defaultMessage: 'Specifies the legend position.', + }), + }, + showSingleSeries: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { + defaultMessage: 'Specifies whether a legend with just a single entry should be shown', + }), + }, + isInside: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.isInside.help', { + defaultMessage: 'Specifies whether a legend is inside the chart', + }), + }, + horizontalAlignment: { + types: ['string'], + options: [HorizontalAlignment.Right, HorizontalAlignment.Left], + help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { + defaultMessage: + 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', + }), + }, + verticalAlignment: { + types: ['string'], + options: [VerticalAlignment.Top, VerticalAlignment.Bottom], + help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { + defaultMessage: + 'Specifies the vertical alignment of the legend when it is displayed inside chart.', + }), + }, + floatingColumns: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { + defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', + }), + }, + }, + fn: function fn(input: unknown, args: LegendConfig) { + return { + type: 'lens_xy_legendConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts b/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts new file mode 100644 index 0000000000000..f9a375b8b47a1 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts @@ -0,0 +1,18 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type SeriesType = + | 'bar' + | 'bar_horizontal' + | 'line' + | 'area' + | 'bar_stacked' + | 'bar_percentage_stacked' + | 'bar_horizontal_stacked' + | 'bar_horizontal_percentage_stacked' + | 'area_stacked' + | 'area_percentage_stacked'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts new file mode 100644 index 0000000000000..4af78d8355786 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts @@ -0,0 +1,51 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { AxesSettingsConfig } from './axis_config'; + +export type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; + +export const tickLabelsConfig: ExpressionFunctionDefinition< + 'lens_xy_tickLabelsConfig', + null, + AxesSettingsConfig, + TickLabelsConfigResult +> = { + name: 'lens_xy_tickLabelsConfig', + aliases: [], + type: 'lens_xy_tickLabelsConfig', + help: `Configure the xy chart's tick labels appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_tickLabelsConfig', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts new file mode 100644 index 0000000000000..3fcf2a187464d --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts @@ -0,0 +1,39 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config'; +import type { FittingFunction } from './fitting_function'; +import type { GridlinesConfigResult } from './grid_lines_config'; +import type { LayerArgs } from './layer_config'; +import type { LegendConfigResult } from './legend_config'; +import type { TickLabelsConfigResult } from './tick_labels_config'; + +export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; + +export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X'; + +// Arguments to XY chart expression, with computed properties +export interface XYArgs { + title?: string; + description?: string; + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + valueLabels: ValueLabelConfig; + layers: LayerArgs[]; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; +} diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts new file mode 100644 index 0000000000000..7fa26f4a2c25d --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts @@ -0,0 +1,156 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common'; +import type { LensMultiTable } from '../../types'; +import type { XYArgs } from './xy_args'; +import { fittingFunctionDefinitions } from './fitting_function'; + +export interface XYChartProps { + data: LensMultiTable; + args: XYArgs; +} + +export interface XYRender { + type: 'render'; + as: 'lens_xy_chart_renderer'; + value: XYChartProps; +} + +export const xyChart: ExpressionFunctionDefinition< + 'lens_xy_chart', + LensMultiTable | ExpressionValueSearchContext | null, + XYArgs, + XYRender +> = { + name: 'lens_xy_chart', + type: 'render', + inputTypes: ['lens_multitable', 'kibana_context', 'null'], + help: i18n.translate('xpack.lens.xyChart.help', { + defaultMessage: 'An X/Y chart', + }), + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + xTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.xTitle.help', { + defaultMessage: 'X axis title', + }), + }, + yTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + }, + yRightTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + }, + yLeftExtent: { + types: ['lens_xy_axisExtentConfig'], + help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { + defaultMessage: 'Y left axis extents', + }), + }, + yRightExtent: { + types: ['lens_xy_axisExtentConfig'], + help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { + defaultMessage: 'Y right axis extents', + }), + }, + legend: { + types: ['lens_xy_legendConfig'], + help: i18n.translate('xpack.lens.xyChart.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + fittingFunction: { + types: ['string'], + options: [...fittingFunctionDefinitions.map(({ id }) => id)], + help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + }, + valueLabels: { + types: ['string'], + options: ['hide', 'inside'], + help: '', + }, + tickLabelsVisibilitySettings: { + types: ['lens_xy_tickLabelsConfig'], + help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + }, + gridlinesVisibilitySettings: { + types: ['lens_xy_gridlinesConfig'], + help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + }, + axisTitlesVisibilitySettings: { + types: ['lens_xy_axisTitlesVisibilityConfig'], + help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + }, + layers: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + types: ['lens_xy_layer'] as any, + help: 'Layers of visual series', + multi: true, + }, + curveType: { + types: ['string'], + options: ['LINEAR', 'CURVE_MONOTONE_X'], + help: i18n.translate('xpack.lens.xyChart.curveType.help', { + defaultMessage: 'Define how curve type is rendered for a line chart', + }), + }, + fillOpacity: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { + defaultMessage: 'Define the area chart fill opacity', + }), + }, + hideEndzones: { + types: ['boolean'], + default: false, + help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { + defaultMessage: 'Hide endzone markers for partial data', + }), + }, + valuesInLegend: { + types: ['boolean'], + default: false, + help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { + defaultMessage: 'Show values in legend', + }), + }, + }, + fn(data: LensMultiTable, args: XYArgs) { + return { + type: 'render', + as: 'lens_xy_chart_renderer', + value: { + data, + args, + }, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/index.ts b/x-pack/plugins/lens/common/index.ts index 25a96d764bb28..42e673058f1db 100644 --- a/x-pack/plugins/lens/common/index.ts +++ b/x-pack/plugins/lens/common/index.ts @@ -8,3 +8,6 @@ export * from './api'; export * from './constants'; export * from './types'; + +// Note: do not import the expression folder here or the page bundle will be bloated with all +// the package diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts b/x-pack/plugins/lens/common/suffix_formatter/index.ts similarity index 93% rename from x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts rename to x-pack/plugins/lens/common/suffix_formatter/index.ts index f21b854128958..12a4e02a81ef2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts +++ b/x-pack/plugins/lens/common/suffix_formatter/index.ts @@ -10,9 +10,9 @@ import { FieldFormat, FieldFormatInstanceType, KBN_FIELD_TYPES, -} from '../../../../../src/plugins/data/public'; -import { FormatFactory } from '../types'; -import { TimeScaleUnit } from './time_scale'; +} from '../../../../../src/plugins/data/common'; +import type { FormatFactory } from '../types'; +import type { TimeScaleUnit } from '../expressions/time_scale'; const unitSuffixes: Record = { s: i18n.translate('xpack.lens.fieldFormats.suffix.s', { defaultMessage: '/s' }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts b/x-pack/plugins/lens/common/suffix_formatter/suffix_formatter.test.ts similarity index 96% rename from x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts rename to x-pack/plugins/lens/common/suffix_formatter/suffix_formatter.test.ts index 4349b95c4deaf..c4379bdd1fb34 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts +++ b/x-pack/plugins/lens/common/suffix_formatter/suffix_formatter.test.ts @@ -6,7 +6,7 @@ */ import { FormatFactory } from '../types'; -import { getSuffixFormatter } from './suffix_formatter'; +import { getSuffixFormatter } from './index'; describe('suffix formatter', () => { it('should call nested formatter and apply suffix', () => { diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index 2ca31c08a4ec7..a60061a3aa054 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { FilterMeta, Filter } from 'src/plugins/data/common'; +import type { FilterMeta, Filter, IFieldFormat } from '../../../../src/plugins/data/common'; +import type { Datatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/common'; + +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; export interface ExistingFields { indexPatternTitle: string; @@ -24,3 +27,32 @@ export interface PersistableFilterMeta extends FilterMeta { export interface PersistableFilter extends Filter { meta: PersistableFilterMeta; } + +export interface LensMultiTable { + type: 'lens_multitable'; + tables: Record; + dateRange?: { + fromDate: Date; + toDate: Date; + }; +} + +export interface ColorStop { + color: string; + stop: number; +} + +export interface CustomPaletteParams { + name?: string; + reverse?: boolean; + rangeType?: 'number' | 'percent'; + continuity?: 'above' | 'below' | 'all' | 'none'; + progression?: 'fixed'; + rangeMin?: number; + rangeMax?: number; + stops?: ColorStop[]; + colorStops?: ColorStop[]; + steps?: number; +} + +export type RequiredPaletteParamTypes = Required; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx index aa8c6ffb26d17..fb9cb992fcf47 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx @@ -14,7 +14,7 @@ import { Datatable } from 'src/plugins/expressions/public'; import { IUiSettingsClient } from 'kibana/public'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import { Args, ColumnConfigArg } from '../expression'; +import { DatatableArgs, ColumnConfigArg } from '../../../common/expressions'; import { DataContextType } from './types'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; @@ -91,7 +91,7 @@ describe('datatable cell renderer', () => { const paletteRegistry = chartPluginMock.createPaletteRegistry(); const customPalette = paletteRegistry.get('custom'); - function getCellRenderer(columnConfig: Args) { + function getCellRenderer(columnConfig: DatatableArgs) { return createGridCell( { a: { convert: (x) => `formatted ${x}` } as FieldFormat, @@ -101,7 +101,7 @@ describe('datatable cell renderer', () => { ({ get: jest.fn() } as unknown) as IUiSettingsClient ); } - function getColumnConfiguration(): Args { + function getColumnConfiguration(): DatatableArgs { return { title: 'myData', columns: [ @@ -136,7 +136,10 @@ describe('datatable cell renderer', () => { }); } - async function renderCellComponent(columnConfig: Args, context: Partial = {}) { + async function renderCellComponent( + columnConfig: DatatableArgs, + context: Partial = {} + ) { const CellRendererWithPalette = getCellRenderer(columnConfig); const setCellProps = jest.fn(); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx index 5a3aa3b45b848..6d6b2e4b1013f 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx @@ -8,11 +8,11 @@ import React, { useContext, useEffect } from 'react'; import { EuiDataGridCellValueElementProps } from '@elastic/eui'; import { IUiSettingsClient } from 'kibana/public'; -import type { FormatFactory } from '../../types'; +import type { FormatFactory } from '../../../common'; +import { getOriginalId } from '../../../common/expressions'; +import type { ColumnConfig } from '../../../common/expressions'; import type { DataContextType } from './types'; -import { ColumnConfig } from './table_basic'; import { getContrastColor, getNumericValue } from '../../shared_components/coloring/utils'; -import { getOriginalId } from '../transpose_helpers'; export const createGridCell = ( formatters: Record>, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx index 4372e2cd9e964..0bc249c783239 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/columns.tsx @@ -13,8 +13,8 @@ import { EuiListGroupItemProps, } from '@elastic/eui'; import type { Datatable, DatatableColumn, DatatableColumnMeta } from 'src/plugins/expressions'; -import type { FormatFactory } from '../../types'; -import { ColumnConfig } from './table_basic'; +import type { FormatFactory } from '../../../common'; +import type { ColumnConfig } from '../../../common/expressions'; export const createGridColumns = ( bucketColumns: string[], diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx index 705484edcf0e6..6840f4f13450c 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.tsx @@ -21,8 +21,7 @@ import { } from '@elastic/eui'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { VisualizationDimensionEditorProps } from '../../types'; -import { ColumnState, DatatableVisualizationState } from '../visualization'; -import { getOriginalId } from '../transpose_helpers'; +import { DatatableVisualizationState } from '../visualization'; import { CustomizablePalette, applyPaletteParams, @@ -33,13 +32,16 @@ import { PalettePanelContainer, findMinMaxByColumnId, } from '../../shared_components/'; -import './dimension_editor.scss'; +import type { ColumnState } from '../../../common/expressions'; import { + isNumericFieldForDatatable, getDefaultSummaryLabel, getFinalSummaryConfiguration, getSummaryRowOptions, -} from '../summary'; -import { isNumericField } from '../utils'; + getOriginalId, +} from '../../../common/expressions'; + +import './dimension_editor.scss'; const idPrefix = htmlIdGenerator()(); @@ -93,7 +95,7 @@ export function TableDimensionEditor( const currentData = frame.activeData?.[state.layerId]; // either read config state or use same logic as chart itself - const isNumeric = isNumericField(currentData, accessor); + const isNumeric = isNumericFieldForDatatable(currentData, accessor); const currentAlignment = column?.alignment || (isNumeric ? 'right' : 'left'); const currentColorMode = column?.colorMode || 'none'; const hasDynamicColoring = currentColorMode !== 'none'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts index 8490d33f83444..bcce2fa2f6f69 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.test.ts @@ -17,9 +17,8 @@ import { createGridHideHandler, createTransposeColumnFilterHandler, } from './table_actions'; -import { LensGridDirection } from './types'; -import { ColumnConfig } from './table_basic'; -import { LensMultiTable } from '../../types'; +import { LensMultiTable } from '../../../common'; +import { LensGridDirection, ColumnConfig } from '../../../common/expressions'; function getDefaultConfig(): ColumnConfig { return { diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts index 8615ed6536316..62c2ec3a7f7fd 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_actions.ts @@ -7,15 +7,11 @@ import type { EuiDataGridSorting } from '@elastic/eui'; import type { Datatable, DatatableColumn } from 'src/plugins/expressions'; -import type { LensFilterEvent, LensMultiTable } from '../../types'; -import type { - LensGridDirection, - LensResizeAction, - LensSortAction, - LensToggleAction, -} from './types'; -import { ColumnConfig } from './table_basic'; -import { getOriginalId } from '../transpose_helpers'; +import type { LensFilterEvent } from '../../types'; +import type { LensMultiTable } from '../../../common'; +import type { LensResizeAction, LensSortAction, LensToggleAction } from './types'; +import type { ColumnConfig, LensGridDirection } from '../../../common/expressions'; +import { getOriginalId } from '../../../common/expressions'; export const createGridResizeHandler = ( columnConfig: ColumnConfig, diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx index ae51f7d42312f..bb678a361e174 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx @@ -15,8 +15,8 @@ import { VisualizationContainer } from '../../visualization_container'; import { EmptyPlaceholder } from '../../shared_components'; import { LensIconChartDatatable } from '../../assets/chart_datatable'; import { DataContext, DatatableComponent } from './table_basic'; -import { LensMultiTable } from '../../types'; -import { DatatableProps } from '../expression'; +import { LensMultiTable } from '../../../common'; +import { DatatableProps } from '../../../common/expressions'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { IUiSettingsClient } from 'kibana/public'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx index 8ef64e4acdccc..ac1324385dbd1 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx @@ -18,18 +18,17 @@ import { EuiDataGridSorting, EuiDataGridStyle, } from '@elastic/eui'; -import { CustomPaletteState, PaletteOutput } from 'src/plugins/charts/common'; -import { FormatFactory, LensFilterEvent, LensTableRowContextMenuEvent } from '../../types'; +import type { LensFilterEvent, LensTableRowContextMenuEvent } from '../../types'; +import type { FormatFactory } from '../../../common'; +import { LensGridDirection } from '../../../common/expressions'; import { VisualizationContainer } from '../../visualization_container'; import { EmptyPlaceholder, findMinMaxByColumnId } from '../../shared_components'; import { LensIconChartDatatable } from '../../assets/chart_datatable'; -import { ColumnState } from '../visualization'; -import { +import type { DataContextType, DatatableRenderProps, LensSortAction, LensResizeAction, - LensGridDirection, LensToggleAction, } from './types'; import { createGridColumns } from './columns'; @@ -42,8 +41,7 @@ import { createTransposeColumnFilterHandler, } from './table_actions'; import { CUSTOM_PALETTE } from '../../shared_components/coloring/constants'; -import { getFinalSummaryConfiguration } from '../summary'; -import { getOriginalId } from '../transpose_helpers'; +import { getOriginalId, getFinalSummaryConfiguration } from '../../../common/expressions'; export const DataContext = React.createContext({}); @@ -52,17 +50,6 @@ const gridStyle: EuiDataGridStyle = { header: 'underline', }; -export interface ColumnConfig { - columns: Array< - Omit & { - type: 'lens_datatable_column'; - palette?: PaletteOutput; - } - >; - sortingColumnId: string | undefined; - sortingDirection: LensGridDirection; -} - export const DatatableComponent = (props: DatatableRenderProps) => { const [firstTable] = Object.values(props.data.tables); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/types.ts b/x-pack/plugins/lens/public/datatable_visualization/components/types.ts index 2095715756a53..f3d81c2d13340 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/types.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/components/types.ts @@ -5,16 +5,14 @@ * 2.0. */ -import type { Direction } from '@elastic/eui'; import { IUiSettingsClient } from 'kibana/public'; import { CustomPaletteState, PaletteRegistry } from 'src/plugins/charts/public'; import type { IAggType } from 'src/plugins/data/public'; import type { Datatable, RenderMode } from 'src/plugins/expressions'; -import type { FormatFactory, ILensInterpreterRenderHandlers, LensEditEvent } from '../../types'; -import type { DatatableProps } from '../expression'; +import type { ILensInterpreterRenderHandlers, LensEditEvent } from '../../types'; import { LENS_EDIT_SORT_ACTION, LENS_EDIT_RESIZE_ACTION, LENS_TOGGLE_ACTION } from './constants'; - -export type LensGridDirection = 'none' | Direction; +import type { FormatFactory } from '../../../common'; +import type { DatatableProps, LensGridDirection } from '../../../common/expressions'; export interface LensSortActionData { columnId: string | undefined; @@ -49,12 +47,6 @@ export type DatatableRenderProps = DatatableProps & { rowHasRowClickTriggerActions?: boolean[]; }; -export interface DatatableRender { - type: 'render'; - as: 'lens_datatable_renderer'; - value: DatatableProps; -} - export interface DataContextType { table?: Datatable; rowHasRowClickTriggerActions?: boolean[]; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 3ba448b49afc9..4b4d2275d0dec 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import { DatatableProps, getDatatable } from './expression'; -import { LensMultiTable } from '../types'; +import { DatatableProps } from '../../common/expressions'; +import type { LensMultiTable } from '../../common'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import { getDatatable } from './expression'; function sampleArgs() { const indexPatternId = 'indexPatternId'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 79a541b0288ab..4e541bce9a8c2 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -7,194 +7,20 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import type { IAggType } from 'src/plugins/data/public'; -import { - DatatableColumnMeta, - ExpressionFunctionDefinition, - ExpressionRenderDefinition, -} from 'src/plugins/expressions'; -import { CustomPaletteState, PaletteOutput } from 'src/plugins/charts/common'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { IUiSettingsClient } from 'kibana/public'; -import { getSortingCriteria } from './sorting'; - +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; import { DatatableComponent } from './components/table_basic'; -import { ColumnState } from './visualization'; - -import type { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types'; -import type { DatatableRender } from './components/types'; -import { transposeTable } from './transpose_helpers'; -import { computeSummaryRowForColumn } from './summary'; - -export type ColumnConfigArg = Omit & { - type: 'lens_datatable_column'; - palette?: PaletteOutput; - summaryRowValue?: unknown; -}; - -export interface Args { - title: string; - description?: string; - columns: ColumnConfigArg[]; - sortingColumnId: string | undefined; - sortingDirection: 'asc' | 'desc' | 'none'; -} - -export interface DatatableProps { - data: LensMultiTable; - untransposedData?: LensMultiTable; - args: Args; -} - -function isRange(meta: { params?: { id?: string } } | undefined) { - return meta?.params?.id === 'range'; -} - -export const getDatatable = ({ - formatFactory, -}: { - formatFactory: FormatFactory; -}): ExpressionFunctionDefinition<'lens_datatable', LensMultiTable, Args, DatatableRender> => ({ - name: 'lens_datatable', - type: 'render', - inputTypes: ['lens_multitable'], - help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', { - defaultMessage: 'Datatable renderer', - }), - args: { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.datatable.titleLabel', { - defaultMessage: 'Title', - }), - }, - description: { - types: ['string'], - help: '', - }, - columns: { - types: ['lens_datatable_column'], - help: '', - multi: true, - }, - sortingColumnId: { - types: ['string'], - help: '', - }, - sortingDirection: { - types: ['string'], - help: '', - }, - }, - fn(data, args, context) { - let untransposedData: LensMultiTable | undefined; - // do the sorting at this level to propagate it also at CSV download - const [firstTable] = Object.values(data.tables); - const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); - const formatters: Record> = {}; - - firstTable.columns.forEach((column) => { - formatters[column.id] = formatFactory(column.meta?.params); - }); - - const hasTransposedColumns = args.columns.some((c) => c.isTransposed); - if (hasTransposedColumns) { - // store original shape of data separately - untransposedData = cloneDeep(data); - // transposes table and args inplace - transposeTable(args, firstTable, formatters); - } - - const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args; - - const columnsReverseLookup = firstTable.columns.reduce< - Record - >((memo, { id, name, meta }, i) => { - memo[id] = { name, index: i, meta }; - return memo; - }, {}); - - const columnsWithSummary = args.columns.filter((c) => c.summaryRow); - for (const column of columnsWithSummary) { - column.summaryRowValue = computeSummaryRowForColumn( - column, - firstTable, - formatters, - formatFactory({ id: 'number' }) - ); - } - - if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') { - // Sort on raw values for these types, while use the formatted value for the rest - const sortingCriteria = getSortingCriteria( - isRange(columnsReverseLookup[sortBy]?.meta) - ? 'range' - : columnsReverseLookup[sortBy]?.meta?.type, - sortBy, - formatters[sortBy], - sortDirection - ); - // replace the table here - context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || []) - .slice() - .sort(sortingCriteria); - // replace also the local copy - firstTable.rows = context.inspectorAdapters.tables[layerId].rows; - } else { - args.sortingColumnId = undefined; - args.sortingDirection = 'none'; - } - return { - type: 'render', - as: 'lens_datatable_renderer', - value: { - data, - untransposedData, - args, - }, - }; - }, -}); -type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' }; +import type { ILensInterpreterRenderHandlers } from '../types'; +import type { FormatFactory } from '../../common'; +import { DatatableProps } from '../../common/expressions'; -export const datatableColumn: ExpressionFunctionDefinition< - 'lens_datatable_column', - null, - ColumnState, - DatatableColumnResult -> = { - name: 'lens_datatable_column', - aliases: [], - type: 'lens_datatable_column', - help: '', - inputTypes: ['null'], - args: { - columnId: { types: ['string'], help: '' }, - alignment: { types: ['string'], help: '' }, - hidden: { types: ['boolean'], help: '' }, - width: { types: ['number'], help: '' }, - isTransposed: { types: ['boolean'], help: '' }, - transposable: { types: ['boolean'], help: '' }, - colorMode: { types: ['string'], help: '' }, - palette: { - types: ['palette'], - help: '', - }, - summaryRow: { types: ['string'], help: '' }, - summaryLabel: { types: ['string'], help: '' }, - }, - fn: function fn(input: unknown, args: ColumnState) { - return { - type: 'lens_datatable_column', - ...args, - }; - }, -}; +export { datatableColumn, getDatatable } from '../../common/expressions'; export const getDatatableRenderer = (dependencies: { formatFactory: FormatFactory; diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 7f48d00d00f7f..b4f37faf0bc00 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ChartsPluginSetup } from 'src/plugins/charts/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import type { CoreSetup } from 'kibana/public'; +import type { ChartsPluginSetup } from 'src/plugins/charts/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import type { FormatFactory } from '../../common'; interface DatatableVisualizationPluginStartPlugins { data: DataPublicPluginStart; diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index e7ab4aab88f2e..691fce0ed70d2 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -10,9 +10,8 @@ import { render } from 'react-dom'; import { Ast } from '@kbn/interpreter/common'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { DatatableColumn } from 'src/plugins/expressions/public'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; -import { +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { SuggestionRequest, Visualization, VisualizationSuggestion, @@ -21,32 +20,9 @@ import { import { LensIconChartDatatable } from '../assets/chart_datatable'; import { TableDimensionEditor } from './components/dimension_editor'; import { CUSTOM_PALETTE } from '../shared_components/coloring/constants'; -import { CustomPaletteParams } from '../shared_components/coloring/types'; import { getStopsForFixedMode } from '../shared_components'; -import { getDefaultSummaryLabel } from './summary'; - -export interface ColumnState { - columnId: string; - width?: number; - hidden?: boolean; - isTransposed?: boolean; - // These flags are necessary to transpose columns and map them back later - // They are set automatically and are not user-editable - transposable?: boolean; - originalColumnId?: string; - originalName?: string; - bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>; - alignment?: 'left' | 'right' | 'center'; - palette?: PaletteOutput; - colorMode?: 'none' | 'cell' | 'text'; - summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max'; - summaryLabel?: string; -} - -export interface SortingState { - columnId: string | undefined; - direction: 'asc' | 'desc' | 'none'; -} +import { getDefaultSummaryLabel } from '../../common/expressions'; +import type { ColumnState, SortingState } from '../../common/expressions'; export interface DatatableVisualizationState { columns: ColumnState[]; diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 63340795ec6c8..b3574f19b5caf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -22,7 +22,7 @@ import { EditorFrameStart, } from '../types'; import { Document } from '../persistence/saved_object_store'; -import { mergeTables } from './merge_tables'; +import { mergeTables } from '../../common/expressions'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index e27abc4ae32ec..e8095f6c741a4 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -23,9 +23,8 @@ import type { LensByReferenceInput, LensByValueInput } from './embeddable'; import type { Document } from '../persistence'; import type { IndexPatternPersistedState } from '../indexpattern_datasource/types'; import type { XYState } from '../xy_visualization/types'; -import type { PieVisualizationState } from '../pie_visualization/types'; +import type { PieVisualizationState, MetricState } from '../../common/expressions'; import type { DatatableVisualizationState } from '../datatable_visualization/visualization'; -import type { MetricState } from '../metric_visualization/types'; type LensAttributes = Omit< Document, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx index 3e8e9d184ed8a..15e9963ff5740 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/chart_component.tsx @@ -16,11 +16,11 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import { CustomPaletteState } from 'src/plugins/charts/public'; +import type { CustomPaletteState } from 'src/plugins/charts/public'; import { VisualizationContainer } from '../visualization_container'; -import { HeatmapRenderProps } from './types'; +import type { HeatmapRenderProps } from './types'; import './index.scss'; -import { LensBrushEvent, LensFilterEvent } from '../types'; +import type { LensBrushEvent, LensFilterEvent } from '../types'; import { applyPaletteParams, defaultPaletteParams, diff --git a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx b/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx index 85daa4805b9ec..ca4a65e6fb10f 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/dimension_editor.tsx @@ -14,8 +14,8 @@ import { EuiFlexGroup, EuiButtonEmpty, } from '@elastic/eui'; -import { PaletteRegistry } from 'src/plugins/charts/public'; -import { VisualizationDimensionEditorProps } from '../types'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { VisualizationDimensionEditorProps } from '../types'; import { CustomizablePalette, FIXED_PROGRESSION, @@ -23,7 +23,7 @@ import { PalettePanelContainer, } from '../shared_components/'; import './dimension_editor.scss'; -import { HeatmapVisualizationState } from './types'; +import type { HeatmapVisualizationState } from './types'; import { getSafePaletteParams } from './utils'; export function HeatmapDimensionEditor( diff --git a/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx b/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx index 37dad117bb783..27be4b9ce7fe9 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/expression.tsx @@ -9,221 +9,15 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import ReactDOM from 'react-dom'; import React from 'react'; -import { Position } from '@elastic/charts'; -import { - ExpressionFunctionDefinition, - IInterpreterRenderHandlers, -} from '../../../../../src/plugins/expressions'; -import { FormatFactory, LensBrushEvent, LensFilterEvent, LensMultiTable } from '../types'; -import { - FUNCTION_NAME, - HEATMAP_GRID_FUNCTION, - LEGEND_FUNCTION, - LENS_HEATMAP_RENDERER, -} from './constants'; -import type { - HeatmapExpressionArgs, - HeatmapExpressionProps, - HeatmapGridConfig, - HeatmapGridConfigResult, - HeatmapRender, - LegendConfigResult, -} from './types'; -import { HeatmapLegendConfig } from './types'; -import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; +import type { IInterpreterRenderHandlers } from '../../../../../src/plugins/expressions'; +import type { LensBrushEvent, LensFilterEvent } from '../types'; +import type { FormatFactory } from '../../common'; +import { LENS_HEATMAP_RENDERER } from './constants'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; import { HeatmapChartReportable } from './chart_component'; +import type { HeatmapExpressionProps } from './types'; -export const heatmapGridConfig: ExpressionFunctionDefinition< - typeof HEATMAP_GRID_FUNCTION, - null, - HeatmapGridConfig, - HeatmapGridConfigResult -> = { - name: HEATMAP_GRID_FUNCTION, - aliases: [], - type: HEATMAP_GRID_FUNCTION, - help: `Configure the heatmap layout `, - inputTypes: ['null'], - args: { - // grid - strokeWidth: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.strokeWidth.help', { - defaultMessage: 'Specifies the grid stroke width', - }), - required: false, - }, - strokeColor: { - types: ['string'], - help: i18n.translate('xpack.lens.heatmapChart.config.strokeColor.help', { - defaultMessage: 'Specifies the grid stroke color', - }), - required: false, - }, - cellHeight: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.cellHeight.help', { - defaultMessage: 'Specifies the grid cell height', - }), - required: false, - }, - cellWidth: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.cellWidth.help', { - defaultMessage: 'Specifies the grid cell width', - }), - required: false, - }, - // cells - isCellLabelVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.config.isCellLabelVisible.help', { - defaultMessage: 'Specifies whether or not the cell label is visible.', - }), - }, - // Y-axis - isYAxisLabelVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.config.isYAxisLabelVisible.help', { - defaultMessage: 'Specifies whether or not the Y-axis labels are visible.', - }), - }, - yAxisLabelWidth: { - types: ['number'], - help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelWidth.help', { - defaultMessage: 'Specifies the width of the Y-axis labels.', - }), - required: false, - }, - yAxisLabelColor: { - types: ['string'], - help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelColor.help', { - defaultMessage: 'Specifies the color of the Y-axis labels.', - }), - required: false, - }, - // X-axis - isXAxisLabelVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.config.isXAxisLabelVisible.help', { - defaultMessage: 'Specifies whether or not the X-axis labels are visible.', - }), - }, - }, - fn(input, args) { - return { - type: HEATMAP_GRID_FUNCTION, - ...args, - }; - }, -}; - -/** - * TODO check if it's possible to make a shared function - * based on the XY chart - */ -export const heatmapLegendConfig: ExpressionFunctionDefinition< - typeof LEGEND_FUNCTION, - null, - HeatmapLegendConfig, - LegendConfigResult -> = { - name: LEGEND_FUNCTION, - aliases: [], - type: LEGEND_FUNCTION, - help: `Configure the heatmap chart's legend`, - inputTypes: ['null'], - args: { - isVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.heatmapChart.legend.isVisible.help', { - defaultMessage: 'Specifies whether or not the legend is visible.', - }), - }, - position: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('xpack.lens.heatmapChart.legend.position.help', { - defaultMessage: 'Specifies the legend position.', - }), - }, - }, - fn(input, args) { - return { - type: LEGEND_FUNCTION, - ...args, - }; - }, -}; - -export const heatmap: ExpressionFunctionDefinition< - typeof FUNCTION_NAME, - LensMultiTable, - HeatmapExpressionArgs, - HeatmapRender -> = { - name: FUNCTION_NAME, - type: 'render', - help: i18n.translate('xpack.lens.heatmap.expressionHelpLabel', { - defaultMessage: 'Heatmap renderer', - }), - args: { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.heatmap.titleLabel', { - defaultMessage: 'Title', - }), - }, - description: { - types: ['string'], - help: '', - }, - xAccessor: { - types: ['string'], - help: '', - }, - yAccessor: { - types: ['string'], - help: '', - }, - valueAccessor: { - types: ['string'], - help: '', - }, - shape: { - types: ['string'], - help: '', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - legend: { - types: [LEGEND_FUNCTION], - help: i18n.translate('xpack.lens.heatmapChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - gridConfig: { - types: [HEATMAP_GRID_FUNCTION], - help: i18n.translate('xpack.lens.heatmapChart.gridConfig.help', { - defaultMessage: 'Configure the heatmap layout.', - }), - }, - }, - inputTypes: ['lens_multitable'], - fn(data: LensMultiTable, args: HeatmapExpressionArgs) { - return { - type: 'render', - as: LENS_HEATMAP_RENDERER, - value: { - data, - args, - }, - }; - }, -}; +export { heatmapGridConfig, heatmapLegendConfig, heatmap } from '../../common/expressions'; export const getHeatmapRenderer = (dependencies: { formatFactory: Promise; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/index.ts b/x-pack/plugins/lens/public/heatmap_visualization/index.ts index 4599bd8d2a208..11f9b907eb929 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/index.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { CoreSetup } from 'kibana/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { getTimeZone } from '../utils'; +import type { FormatFactory } from '../../common'; export interface HeatmapVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts index c11078be6c8b9..d7443ea8fe43d 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.test.ts @@ -5,10 +5,10 @@ * 2.0. */ +import { Position } from '@elastic/charts'; import { getSuggestions } from './suggestions'; -import { HeatmapVisualizationState } from './types'; +import type { HeatmapVisualizationState } from './types'; import { HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants'; -import { Position } from '@elastic/charts'; describe('heatmap suggestions', () => { describe('rejects suggestions', () => { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts index 5cddebe2cc230..3f27d5e81b507 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/suggestions.ts @@ -8,8 +8,8 @@ import { partition } from 'lodash'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Visualization } from '../types'; -import { HeatmapVisualizationState } from './types'; +import type { Visualization } from '../types'; +import type { HeatmapVisualizationState } from './types'; import { CHART_SHAPES, HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants'; export const getSuggestions: Visualization['getSuggestions'] = ({ diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx index 6fd863ba91936..c35143773551d 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx @@ -9,9 +9,9 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { VisualizationToolbarProps } from '../types'; +import type { VisualizationToolbarProps } from '../types'; import { LegendSettingsPopover } from '../shared_components'; -import { HeatmapVisualizationState } from './types'; +import type { HeatmapVisualizationState } from './types'; const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [ { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/types.ts b/x-pack/plugins/lens/public/heatmap_visualization/types.ts index 32e3079c951d4..0cf830bea609a 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/types.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/types.ts @@ -5,17 +5,12 @@ * 2.0. */ -import { Position } from '@elastic/charts'; -import { PaletteOutput } from '../../../../../src/plugins/charts/common'; -import { FormatFactory, LensBrushEvent, LensFilterEvent, LensMultiTable } from '../types'; -import { - CHART_SHAPES, - HEATMAP_GRID_FUNCTION, - LEGEND_FUNCTION, - LENS_HEATMAP_RENDERER, -} from './constants'; -import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; -import { CustomPaletteParams } from '../shared_components'; +import type { PaletteOutput } from '../../../../../src/plugins/charts/common'; +import type { LensBrushEvent, LensFilterEvent } from '../types'; +import type { LensMultiTable, FormatFactory, CustomPaletteParams } from '../../common'; +import type { HeatmapGridConfigResult, HeatmapLegendConfigResult } from '../../common/expressions'; +import { CHART_SHAPES, LENS_HEATMAP_RENDERER } from './constants'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; export type ChartShapes = typeof CHART_SHAPES[keyof typeof CHART_SHAPES]; @@ -24,7 +19,7 @@ export interface SharedHeatmapLayerState { xAccessor?: string; yAccessor?: string; valueAccessor?: string; - legend: LegendConfigResult; + legend: HeatmapLegendConfigResult; gridConfig: HeatmapGridConfigResult; } @@ -62,34 +57,3 @@ export type HeatmapRenderProps = HeatmapExpressionProps & { onSelectRange: (data: LensBrushEvent['data']) => void; paletteService: PaletteRegistry; }; - -export interface HeatmapLegendConfig { - /** - * Flag whether the legend should be shown. If there is just a single series, it will be hidden - */ - isVisible: boolean; - /** - * Position of the legend relative to the chart - */ - position: Position; -} - -export type LegendConfigResult = HeatmapLegendConfig & { type: typeof LEGEND_FUNCTION }; - -export interface HeatmapGridConfig { - // grid - strokeWidth?: number; - strokeColor?: string; - cellHeight?: number; - cellWidth?: number; - // cells - isCellLabelVisible: boolean; - // Y-axis - isYAxisLabelVisible: boolean; - yAxisLabelWidth?: number; - yAxisLabelColor?: string; - // X-axis - isXAxisLabelVisible: boolean; -} - -export type HeatmapGridConfigResult = HeatmapGridConfig & { type: typeof HEATMAP_GRID_FUNCTION }; diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts b/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts index 316a3ef36d66c..6cbe27fbf323f 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts @@ -19,8 +19,8 @@ import { LEGEND_FUNCTION, } from './constants'; import { Position } from '@elastic/charts'; -import { HeatmapVisualizationState } from './types'; -import { DatasourcePublicAPI, Operation } from '../types'; +import type { HeatmapVisualizationState } from './types'; +import type { DatasourcePublicAPI, Operation } from '../types'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; function exampleState(): HeatmapVisualizationState { diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx index 12fe28f801ef2..716792805e1b5 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx @@ -12,8 +12,8 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { Ast } from '@kbn/interpreter/common'; import { Position } from '@elastic/charts'; import { PaletteRegistry } from '../../../../../src/plugins/charts/public'; -import { OperationMetadata, Visualization } from '../types'; -import { HeatmapVisualizationState } from './types'; +import type { OperationMetadata, Visualization } from '../types'; +import type { HeatmapVisualizationState } from './types'; import { getSuggestions } from './suggestions'; import { CHART_NAMES, @@ -27,9 +27,10 @@ import { } from './constants'; import { HeatmapToolbar } from './toolbar_component'; import { LensIconChartHeatmap } from '../assets/chart_heatmap'; -import { CustomPaletteParams, CUSTOM_PALETTE, getStopsForFixedMode } from '../shared_components'; +import { CUSTOM_PALETTE, getStopsForFixedMode } from '../shared_components'; import { HeatmapDimensionEditor } from './dimension_editor'; import { getSafePaletteParams } from './utils'; +import type { CustomPaletteParams } from '../../common'; const groupLabelForBar = i18n.translate('xpack.lens.heatmapVisualization.heatmapGroupLabel', { defaultMessage: 'Heatmap', diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 76afe7260a35a..84302f25d0a02 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,8 +11,13 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; +export type { XYState } from './xy_visualization/types'; +export type { DataType, OperationMetadata } from './types'; export type { - XYState, + PieVisualizationState, + PieLayerState, + SharedPieLayerState, + MetricState, AxesSettingsConfig, XYLayerConfig, LegendConfig, @@ -21,15 +26,8 @@ export type { YAxisMode, XYCurveType, YConfig, -} from './xy_visualization/types'; -export type { DataType, OperationMetadata } from './types'; -export type { - PieVisualizationState, - PieLayerState, - SharedPieLayerState, -} from './pie_visualization/types'; +} from '../common/expressions'; export type { DatatableVisualizationState } from './datatable_visualization/visualization'; -export type { MetricState } from './metric_visualization/types'; export type { IndexPatternPersistedState, PersistedIndexPatternLayer, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index 3965f992805b5..d6091557ce235 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -16,7 +16,7 @@ import { IndexPatternColumn } from '../indexpattern'; import { isColumnInvalid } from '../utils'; import { IndexPatternPrivateState } from '../types'; import { DimensionEditor } from './dimension_editor'; -import { DateRange } from '../../../common'; +import type { DateRange } from '../../../common'; import { getOperationSupportMatrix } from './operation_support'; export type IndexPatternDimensionTriggerProps = DatasourceDimensionTriggerProps & { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx index 61e5da5931e88..7c611230683d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/time_scaling.tsx @@ -15,8 +15,8 @@ import { IndexPatternColumn, operationDefinitionMap, } from '../operations'; -import { unitSuffixesLong } from '../suffix_formatter'; -import { TimeScaleUnit } from '../time_scale'; +import type { TimeScaleUnit } from '../../../common/expressions'; +import { unitSuffixesLong } from '../../../common/suffix_formatter'; import { IndexPatternLayer } from '../types'; export function setTimeScaling( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index f8bc84643bcab..35afd28c0f1ab 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -43,14 +43,14 @@ export class IndexPatternDatasource { renameColumns, formatColumn, counterRate, - getTimeScaleFunction, + timeScale, getSuffixFormatter, } = await import('../async_services'); return core .getStartServices() .then(([coreStart, { data, indexPatternFieldEditor, uiActions }]) => { data.fieldFormats.register([getSuffixFormatter(data.fieldFormats.deserialize)]); - expressions.registerFunction(getTimeScaleFunction(data)); + expressions.registerFunction(timeScale); expressions.registerFunction(counterRate); expressions.registerFunction(renameColumns); expressions.registerFunction(formatColumn); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 34be770a7f50d..2cbe801a5b7b8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -69,11 +69,15 @@ export function columnToOperation(column: IndexPatternColumn, uniqueLabel?: stri }; } -export * from './rename_columns'; -export * from './format_column'; -export * from './time_scale'; -export * from './counter_rate'; -export * from './suffix_formatter'; +export { + CounterRateArgs, + ExpressionFunctionCounterRate, + counterRate, +} from '../../common/expressions'; +export { FormatColumnArgs, supportedFormats, formatColumn } from '../../common/expressions'; +export { getSuffixFormatter, unitSuffixesLong } from '../../common/suffix_formatter'; +export { timeScale, TimeScaleArgs } from '../../common/expressions'; +export { renameColumns } from '../../common/expressions'; export function getIndexPatternDatasource({ core, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts index 87116f71919b5..34b33d35d4139 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionAST } from '@kbn/interpreter/common'; import memoizeOne from 'memoize-one'; -import type { TimeScaleUnit } from '../../../time_scale'; +import type { TimeScaleUnit } from '../../../../../common/expressions'; import type { IndexPattern, IndexPatternLayer } from '../../../types'; import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils'; import type { ReferenceBasedIndexPatternColumn } from '../column_types'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts index ae606a5851665..15bd7d4242b92 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts @@ -7,7 +7,7 @@ import { Query } from 'src/plugins/data/public'; import type { Operation } from '../../../types'; -import { TimeScaleUnit } from '../../time_scale'; +import type { TimeScaleUnit } from '../../../../common/expressions'; import type { OperationType } from '../definitions'; export interface BaseIndexPatternColumn extends Operation { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index fd21258002808..cb737d694295d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -17,7 +17,7 @@ import { RangeEditor } from './range_editor'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { updateColumnParam } from '../../layer_helpers'; -import { supportedFormats } from '../../../format_column'; +import { supportedFormats } from '../../../../../common/expressions'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; import { getInvalidFieldMessage, isValidNumber } from '../helpers'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index b5b1960b7b769..1e0d0792e132a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -32,7 +32,7 @@ import { getSortScoreByPriority } from './operations'; import { generateId } from '../../id_generator'; import { ReferenceBasedIndexPatternColumn } from './definitions/column_types'; import { FormulaIndexPatternColumn, regenerateLayerFromAst } from './definitions/formula'; -import { TimeScaleUnit } from '../time_scale'; +import type { TimeScaleUnit } from '../../../common/expressions'; interface ColumnAdvancedParams { filter?: Query | undefined; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts index 152fcaa457c3b..dbdfd5c564125 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts @@ -6,7 +6,7 @@ */ import type { IndexPatternLayer } from '../types'; -import type { TimeScaleUnit } from '../time_scale'; +import type { TimeScaleUnit } from '../../../common/expressions'; import type { IndexPatternColumn } from './definitions'; import { adjustTimeScaleLabelSuffix, adjustTimeScaleOnOtherColumnChange } from './time_scale_utils'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts index a0b61060b9f3a..a6c056933f022 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { unitSuffixesLong } from '../suffix_formatter'; -import type { TimeScaleUnit } from '../time_scale'; +import { unitSuffixesLong } from '../../../common/suffix_formatter'; +import type { TimeScaleUnit } from '../../../common/expressions'; import type { IndexPatternLayer } from '../types'; import type { IndexPatternColumn } from './definitions'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts b/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts deleted file mode 100644 index 368e06110efc9..0000000000000 --- a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import moment from 'moment-timezone'; -import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { search } from '../../../../../src/plugins/data/public'; -import { buildResultColumns } from '../../../../../src/plugins/expressions/common'; - -export type TimeScaleUnit = 's' | 'm' | 'h' | 'd'; - -export interface TimeScaleArgs { - dateColumnId: string; - inputColumnId: string; - outputColumnId: string; - targetUnit: TimeScaleUnit; - outputColumnName?: string; -} - -const unitInMs: Record = { - s: 1000, - m: 1000 * 60, - h: 1000 * 60 * 60, - d: 1000 * 60 * 60 * 24, -}; - -export function getTimeScaleFunction(data: DataPublicPluginStart) { - const timeScale: ExpressionFunctionDefinition< - 'lens_time_scale', - Datatable, - TimeScaleArgs, - Promise - > = { - name: 'lens_time_scale', - type: 'datatable', - help: '', - args: { - dateColumnId: { - types: ['string'], - help: '', - required: true, - }, - inputColumnId: { - types: ['string'], - help: '', - required: true, - }, - outputColumnId: { - types: ['string'], - help: '', - required: true, - }, - outputColumnName: { - types: ['string'], - help: '', - }, - targetUnit: { - types: ['string'], - options: ['s', 'm', 'h', 'd'], - help: '', - required: true, - }, - }, - inputTypes: ['datatable'], - async fn( - input, - { dateColumnId, inputColumnId, outputColumnId, outputColumnName, targetUnit }: TimeScaleArgs - ) { - const dateColumnDefinition = input.columns.find((column) => column.id === dateColumnId); - - if (!dateColumnDefinition) { - throw new Error( - i18n.translate('xpack.lens.functions.timeScale.dateColumnMissingMessage', { - defaultMessage: 'Specified dateColumnId {columnId} does not exist.', - values: { - columnId: dateColumnId, - }, - }) - ); - } - - const resultColumns = buildResultColumns( - input, - outputColumnId, - inputColumnId, - outputColumnName, - { allowColumnOverwrite: true } - ); - - if (!resultColumns) { - return input; - } - - const targetUnitInMs = unitInMs[targetUnit]; - const timeInfo = search.aggs.getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition); - const intervalDuration = timeInfo?.interval && search.aggs.parseInterval(timeInfo.interval); - - if (!timeInfo || !intervalDuration) { - throw new Error( - i18n.translate('xpack.lens.functions.timeScale.timeInfoMissingMessage', { - defaultMessage: 'Could not fetch date histogram information', - }) - ); - } - // the datemath plugin always parses dates by using the current default moment time zone. - // to use the configured time zone, we are switching just for the bounds calculation. - const defaultTimezone = moment().zoneName(); - moment.tz.setDefault(timeInfo.timeZone); - - const timeBounds = - timeInfo.timeRange && data.query.timefilter.timefilter.calculateBounds(timeInfo.timeRange); - - const result = { - ...input, - columns: resultColumns, - rows: input.rows.map((row) => { - const newRow = { ...row }; - - let startOfBucket = moment(row[dateColumnId]); - let endOfBucket = startOfBucket.clone().add(intervalDuration); - if (timeBounds && timeBounds.min) { - startOfBucket = moment.max(startOfBucket, timeBounds.min); - } - if (timeBounds && timeBounds.max) { - endOfBucket = moment.min(endOfBucket, timeBounds.max); - } - const bucketSize = endOfBucket.diff(startOfBucket); - const factor = bucketSize / targetUnitInMs; - - const currentValue = newRow[inputColumnId]; - if (currentValue != null) { - newRow[outputColumnId] = Number(currentValue) / factor; - } - - return newRow; - }), - }; - // reset default moment timezone - moment.tz.setDefault(defaultTimezone); - - return result; - }, - }; - return timeScale; -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index b6f5c364e2d04..69b60711e5186 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -22,9 +22,10 @@ import { import { IndexPatternColumn } from './indexpattern'; import { operationDefinitionMap } from './operations'; import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types'; -import { OriginalColumn } from './rename_columns'; import { dateHistogramOperation } from './operations/definitions'; +type OriginalColumn = { id: string } & IndexPatternColumn; + function getExpressionForLayer( layer: IndexPatternLayer, indexPattern: IndexPattern, diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx index 27a7659b1c817..21c68a9fe1d82 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import { metricChart, MetricChart } from './expression'; -import { LensMultiTable } from '../types'; +import { MetricChart, metricChart } from './expression'; +import { MetricConfig } from '../../common/expressions'; import React from 'react'; import { shallow } from 'enzyme'; -import { MetricConfig } from './types'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import type { LensMultiTable } from '../../common'; function sampleArgs() { const data: LensMultiTable = { diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx index cf6921b2ca579..5a1e0d7fb5bdf 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.tsx @@ -9,75 +9,19 @@ import './expression.scss'; import { I18nProvider } from '@kbn/i18n/react'; import React from 'react'; import ReactDOM from 'react-dom'; -import { - ExpressionFunctionDefinition, +import type { ExpressionRenderDefinition, IInterpreterRenderHandlers, } from '../../../../../src/plugins/expressions/public'; -import { MetricConfig } from './types'; -import { FormatFactory, LensMultiTable } from '../types'; import { AutoScale } from './auto_scale'; import { VisualizationContainer } from '../visualization_container'; import { EmptyPlaceholder } from '../shared_components'; import { LensIconChartMetric } from '../assets/chart_metric'; +import type { FormatFactory } from '../../common'; +import type { MetricChartProps } from '../../common/expressions'; -export interface MetricChartProps { - data: LensMultiTable; - args: MetricConfig; -} - -export interface MetricRender { - type: 'render'; - as: 'lens_metric_chart_renderer'; - value: MetricChartProps; -} - -export const metricChart: ExpressionFunctionDefinition< - 'lens_metric_chart', - LensMultiTable, - Omit, - MetricRender -> = { - name: 'lens_metric_chart', - type: 'render', - help: 'A metric chart', - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - metricTitle: { - types: ['string'], - help: 'The title of the metric shown.', - }, - accessor: { - types: ['string'], - help: 'The column whose value is being displayed', - }, - mode: { - types: ['string'], - options: ['reduced', 'full'], - default: 'full', - help: - 'The display mode of the chart - reduced will only show the metric itself without min size', - }, - }, - inputTypes: ['lens_multitable'], - fn(data, args) { - return { - type: 'render', - as: 'lens_metric_chart_renderer', - value: { - data, - args, - }, - } as MetricRender; - }, -}; +export { metricChart } from '../../common/expressions'; +export type { MetricState, MetricConfig } from '../../common/expressions'; export const getMetricChartRenderer = ( formatFactory: Promise diff --git a/x-pack/plugins/lens/public/metric_visualization/index.ts b/x-pack/plugins/lens/public/metric_visualization/index.ts index c94063ed0bd74..484dc6140ecf2 100644 --- a/x-pack/plugins/lens/public/metric_visualization/index.ts +++ b/x-pack/plugins/lens/public/metric_visualization/index.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; +import type { CoreSetup } from 'kibana/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { FormatFactory } from '../../common'; export interface MetricVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts b/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts index e7f510f106fff..d07dccb770196 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts +++ b/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts @@ -6,7 +6,7 @@ */ import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../types'; -import { MetricState } from './types'; +import type { MetricState } from '../../common/expressions'; import { LensIconChartMetric } from '../assets/chart_metric'; /** diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts index 2882d9c4c0246..2c359d139bb3b 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts @@ -6,7 +6,7 @@ */ import { metricVisualization } from './visualization'; -import { MetricState } from './types'; +import type { MetricState } from '../../common/expressions'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { generateId } from '../id_generator'; import { DatasourcePublicAPI, FramePublicAPI } from '../types'; diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx index 49565f53bda36..d312030b5a490 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx @@ -10,7 +10,7 @@ import { Ast } from '@kbn/interpreter/target/common'; import { getSuggestions } from './metric_suggestions'; import { LensIconChartMetric } from '../assets/chart_metric'; import { Visualization, OperationMetadata, DatasourcePublicAPI } from '../types'; -import { MetricState } from './types'; +import type { MetricState } from '../../common/expressions'; const toExpression = ( state: MetricState, diff --git a/x-pack/plugins/lens/public/pie_visualization/expression.tsx b/x-pack/plugins/lens/public/pie_visualization/expression.tsx index 208ce746f4b9d..ce36f88b2805e 100644 --- a/x-pack/plugins/lens/public/pie_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/expression.tsx @@ -8,108 +8,18 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; -import { Position } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; -import { +import type { IInterpreterRenderHandlers, ExpressionRenderDefinition, - ExpressionFunctionDefinition, } from 'src/plugins/expressions/public'; -import { LensMultiTable, FormatFactory, LensFilterEvent } from '../types'; -import { PieExpressionProps, PieExpressionArgs } from './types'; +import type { LensFilterEvent } from '../types'; import { PieComponent } from './render_function'; -import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; +import type { FormatFactory } from '../../common'; +import type { PieExpressionProps } from '../../common/expressions'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public'; -export interface PieRender { - type: 'render'; - as: 'lens_pie_renderer'; - value: PieExpressionProps; -} - -export const pie: ExpressionFunctionDefinition< - 'lens_pie', - LensMultiTable, - PieExpressionArgs, - PieRender -> = { - name: 'lens_pie', - type: 'render', - help: i18n.translate('xpack.lens.pie.expressionHelpLabel', { - defaultMessage: 'Pie renderer', - }), - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - groups: { - types: ['string'], - multi: true, - help: '', - }, - metric: { - types: ['string'], - help: '', - }, - shape: { - types: ['string'], - options: ['pie', 'donut', 'treemap'], - help: '', - }, - hideLabels: { - types: ['boolean'], - help: '', - }, - numberDisplay: { - types: ['string'], - options: ['hidden', 'percent', 'value'], - help: '', - }, - categoryDisplay: { - types: ['string'], - options: ['default', 'inside', 'hide'], - help: '', - }, - legendDisplay: { - types: ['string'], - options: ['default', 'show', 'hide'], - help: '', - }, - nestedLegend: { - types: ['boolean'], - help: '', - }, - legendPosition: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: '', - }, - percentDecimals: { - types: ['number'], - help: '', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - }, - inputTypes: ['lens_multitable'], - fn(data: LensMultiTable, args: PieExpressionArgs) { - return { - type: 'render', - as: 'lens_pie_renderer', - value: { - data, - args, - }, - }; - }, -}; +export { pie } from '../../common/expressions'; export const getPieRenderer = (dependencies: { formatFactory: Promise; diff --git a/x-pack/plugins/lens/public/pie_visualization/index.ts b/x-pack/plugins/lens/public/pie_visualization/index.ts index 9f4a176ef8aa8..aa74eb5088ea3 100644 --- a/x-pack/plugins/lens/public/pie_visualization/index.ts +++ b/x-pack/plugins/lens/public/pie_visualization/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup } from 'src/core/public'; -import { ExpressionsSetup } from 'src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { CoreSetup } from 'src/core/public'; +import type { ExpressionsSetup } from 'src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { FormatFactory } from '../../common'; export interface PieVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index a3a10b803fcd3..adef7188d12d0 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -16,9 +16,9 @@ import { Chart, } from '@elastic/charts'; import { shallow } from 'enzyme'; -import { LensMultiTable } from '../types'; +import type { LensMultiTable } from '../../common'; +import type { PieExpressionArgs } from '../../common/expressions'; import { PieComponent } from './render_function'; -import { PieExpressionArgs } from './types'; import { VisualizationContainer } from '../visualization_container'; import { EmptyPlaceholder } from '../shared_components'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index b161a81a835f1..ac0aa6cd4b1f1 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -24,10 +24,11 @@ import { ElementClickListener, } from '@elastic/charts'; import { RenderMode } from 'src/plugins/expressions'; -import { FormatFactory, LensFilterEvent } from '../types'; +import type { LensFilterEvent } from '../types'; import { VisualizationContainer } from '../visualization_container'; import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants'; -import { PieExpressionProps } from './types'; +import type { FormatFactory } from '../../common'; +import type { PieExpressionProps } from '../../common/expressions'; import { getSliceValue, getFilterContext } from './render_helpers'; import { EmptyPlaceholder } from '../shared_components'; import './visualization.scss'; diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts index a527a3c864543..36470fa3d74cf 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts @@ -8,7 +8,7 @@ import { PaletteOutput } from 'src/plugins/charts/public'; import { DataType, SuggestionRequest } from '../types'; import { suggestions } from './suggestions'; -import { PieVisualizationState } from './types'; +import type { PieVisualizationState } from '../../common/expressions'; describe('suggestions', () => { describe('pie', () => { diff --git a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts index 644f0a0cd8aaf..22be8e3357bbb 100644 --- a/x-pack/plugins/lens/public/pie_visualization/suggestions.ts +++ b/x-pack/plugins/lens/public/pie_visualization/suggestions.ts @@ -7,8 +7,8 @@ import { partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { SuggestionRequest, VisualizationSuggestion } from '../types'; -import { PieVisualizationState } from './types'; +import type { SuggestionRequest, VisualizationSuggestion } from '../types'; +import type { PieVisualizationState } from '../../common/expressions'; import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants'; function shouldReject({ table, keptLayerIds }: SuggestionRequest) { diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index 14f1fe81c7bd6..7ee26383cebbf 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -9,7 +9,7 @@ import { Ast } from '@kbn/interpreter/common'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { Operation, DatasourcePublicAPI } from '../types'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; -import { PieVisualizationState } from './types'; +import type { PieVisualizationState } from '../../common/expressions'; export function toExpression( state: PieVisualizationState, diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx index a2596f7ce1e0f..5da69e47f861c 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx @@ -15,10 +15,10 @@ import { EuiRange, EuiHorizontalRule, } from '@elastic/eui'; -import { Position } from '@elastic/charts'; -import { PaletteRegistry } from 'src/plugins/charts/public'; +import type { Position } from '@elastic/charts'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; -import { PieVisualizationState, SharedPieLayerState } from './types'; +import type { PieVisualizationState, SharedPieLayerState } from '../../common/expressions'; import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../types'; import { ToolbarPopover, LegendSettingsPopover, useDebouncedValue } from '../shared_components'; import { PalettePicker } from '../shared_components'; diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts b/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts index 2a961cef315bf..07a4161e7d239 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/visualization.test.ts @@ -6,7 +6,7 @@ */ import { getPieVisualization } from './visualization'; -import { PieVisualizationState } from './types'; +import type { PieVisualizationState } from '../../common/expressions'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; jest.mock('../id_generator'); diff --git a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx index c82fdb2766f7e..5d75d82220d1f 100644 --- a/x-pack/plugins/lens/public/pie_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { render } from 'react-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import { PaletteRegistry } from 'src/plugins/charts/public'; -import { Visualization, OperationMetadata, AccessorConfig } from '../types'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { Visualization, OperationMetadata, AccessorConfig } from '../types'; import { toExpression, toPreviewExpression } from './to_expression'; -import { PieLayerState, PieVisualizationState } from './types'; +import type { PieLayerState, PieVisualizationState } from '../../common/expressions'; import { suggestions } from './suggestions'; import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants'; import { DimensionEditor, PieToolbar } from './toolbar'; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx index 37197b232ddf5..1431e6ad135be 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx @@ -22,7 +22,7 @@ import useUnmount from 'react-use/lib/useUnmount'; import { DEFAULT_COLOR } from './constants'; import { getDataMinMax, getStepValue, isValidColor } from './utils'; import { TooltipWrapper, useDebouncedValue } from '../index'; -import { ColorStop, CustomPaletteParams } from './types'; +import type { ColorStop, CustomPaletteParams } from '../../../common'; const idGeneratorFn = htmlIdGenerator(); diff --git a/x-pack/plugins/lens/public/shared_components/coloring/constants.ts b/x-pack/plugins/lens/public/shared_components/coloring/constants.ts index 5e6fc207656ac..29b50d3aee22d 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/constants.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/constants.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RequiredPaletteParamTypes } from './types'; +import type { RequiredPaletteParamTypes } from '../../../common'; export const DEFAULT_PALETTE_NAME = 'positive'; export const FIXED_PROGRESSION = 'fixed' as const; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/index.ts b/x-pack/plugins/lens/public/shared_components/coloring/index.ts index 0ad831603ab95..7cbf79ac43b1e 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/index.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/index.ts @@ -8,6 +8,5 @@ export { CustomizablePalette } from './palette_configuration'; export { PalettePanelContainer } from './palette_panel_container'; export { CustomStops } from './color_stops'; -export * from './types'; export * from './utils'; export * from './constants'; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx index e36e817b3d714..ad1755bdbe85c 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { EuiColorPalettePickerPaletteProps } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test/jest'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import { ReactWrapper } from 'enzyme'; -import { CustomPaletteParams } from './types'; +import type { CustomPaletteParams } from '../../../common'; import { applyPaletteParams } from './utils'; import { CustomizablePalette } from './palette_configuration'; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx index 993bf4f78dd20..bc6a590db0cb7 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx @@ -6,7 +6,7 @@ */ import React, { FC } from 'react'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import { EuiFormRow, htmlIdGenerator, @@ -26,7 +26,7 @@ import './palette_configuration.scss'; import { CustomStops } from './color_stops'; import { defaultPaletteParams, CUSTOM_PALETTE, DEFAULT_COLOR_STEPS } from './constants'; -import { CustomPaletteParams, RequiredPaletteParamTypes } from './types'; +import type { CustomPaletteParams, RequiredPaletteParamTypes } from '../../../common'; import { getColorStops, getPaletteStops, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx index 1371fbe73ef84..583d6e25ed4e2 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx @@ -7,6 +7,7 @@ import './palette_panel_container.scss'; +import { i18n } from '@kbn/i18n'; import React, { useState, useEffect, MutableRefObject } from 'react'; import { EuiFlyoutHeader, @@ -21,8 +22,6 @@ import { EuiPortal, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - export function PalettePanelContainer({ isOpen, handleClose, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx index 164ed9bf067a6..2a415cd178925 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_picker.tsx @@ -13,9 +13,9 @@ import { DEFAULT_COLOR_STEPS, FIXED_PROGRESSION, defaultPaletteParams, -} from '../../shared_components/coloring/constants'; -import { CustomPaletteParams } from '../../shared_components/coloring/types'; -import { getStopsForFixedMode } from '../../shared_components/coloring/utils'; +} from './constants'; +import type { CustomPaletteParams } from '../../../common'; +import { getStopsForFixedMode } from './utils'; function getCustomPaletteConfig( palettes: PaletteRegistry, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/types.ts b/x-pack/plugins/lens/public/shared_components/coloring/types.ts deleted file mode 100644 index d9a8edf0ccb62..0000000000000 --- a/x-pack/plugins/lens/public/shared_components/coloring/types.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -export interface ColorStop { - color: string; - stop: number; -} - -export interface CustomPaletteParams { - name?: string; - reverse?: boolean; - rangeType?: 'number' | 'percent'; - continuity?: 'above' | 'below' | 'all' | 'none'; - progression?: 'fixed'; - rangeMin?: number; - rangeMax?: number; - stops?: ColorStop[]; - colorStops?: ColorStop[]; - steps?: number; -} - -export type RequiredPaletteParamTypes = Required; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/utils.ts b/x-pack/plugins/lens/public/shared_components/coloring/utils.ts index 9c42ce5013b9b..8cd0a6cf49001 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/utils.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/utils.ts @@ -9,7 +9,7 @@ import chroma from 'chroma-js'; import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; import { euiLightVars, euiDarkVars } from '@kbn/ui-shared-deps/theme'; import { isColorDark } from '@elastic/eui'; -import { Datatable } from 'src/plugins/expressions/public'; +import type { Datatable } from 'src/plugins/expressions/public'; import { CUSTOM_PALETTE, defaultPaletteParams, @@ -17,7 +17,7 @@ import { DEFAULT_MAX_STOP, DEFAULT_MIN_STOP, } from './constants'; -import { CustomPaletteParams, ColorStop } from './types'; +import type { CustomPaletteParams, ColorStop } from '../../../common'; /** * Some name conventions here: diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index cb47dcf6ec388..3d87d234ae986 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -16,11 +16,10 @@ import { ExpressionRendererEvent, IInterpreterRenderHandlers, Datatable, - SerializedFieldFormat, } from '../../../../src/plugins/expressions/public'; import { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import { DateRange } from '../common'; -import { Query, Filter, IFieldFormat } from '../../../../src/plugins/data/public'; +import { Query, Filter } from '../../../../src/plugins/data/public'; import { VisualizeFieldContext } from '../../../../src/plugins/ui_actions/public'; import { RangeSelectContext, ValueClickContext } from '../../../../src/plugins/embeddable/public'; import { @@ -37,8 +36,6 @@ import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; export type ErrorCallback = (e: { message: string }) => void; -export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; - export interface PublicAPIProps { state: T; layerId: string; @@ -387,15 +384,6 @@ export interface OperationMetadata { // introduce a raw document datasource, this should be considered here. } -export interface LensMultiTable { - type: 'lens_multitable'; - tables: Record; - dateRange?: { - fromDate: Date; - toDate: Date; - }; -} - export interface VisualizationConfigProps { layerId: string; frame: Pick; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index b82afeb1b7d1d..873827700d6e8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LayerArgs } from './types'; +import { LayerArgs } from '../../common/expressions'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 58a80ad0fed37..83d86eb410b19 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { AxisExtentConfig, XYLayerConfig } from './types'; +import { FormatFactory } from '../../common'; +import { AxisExtentConfig, XYLayerConfig } from '../../common/expressions'; import { Datatable, SerializedFieldFormat } from '../../../../../src/plugins/expressions/public'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; @@ -33,7 +34,7 @@ export function getAxesConfiguration( layers: XYLayerConfig[], shouldRotate: boolean, tables?: Record, - formatFactory?: (mapping: SerializedFieldFormat) => IFieldFormat + formatFactory?: FormatFactory ): GroupsConfiguration { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; right: FormattedMetric[] } = { auto: [], diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx index a0d1dae2145d5..52098ab92cad6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx @@ -20,7 +20,7 @@ import { EuiFieldNumber, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from './types'; +import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from '../../common/expressions'; import { ToolbarPopover, useDebouncedValue } from '../shared_components'; import { isHorizontalChart } from './state_helpers'; import { EuiIconAxisBottom } from '../assets/axis_bottom'; diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index 04a64d7ff5c93..390eded97d705 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { FormatFactory, LensMultiTable } from '../types'; import { getColorAssignments } from './color_assignment'; -import { LayerArgs } from './types'; +import type { FormatFactory, LensMultiTable } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; describe('color_assignment', () => { const layers: LayerArgs[] = [ diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index ef0c350f20961..1e00d821d9b30 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -6,11 +6,12 @@ */ import { uniq, mapValues } from 'lodash'; -import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; -import { Datatable } from 'src/plugins/expressions'; -import { AccessorConfig, FormatFactory, FramePublicAPI } from '../types'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { Datatable } from 'src/plugins/expressions'; +import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import { XYLayerConfig } from './types'; +import type { FormatFactory } from '../../common'; +import type { XYLayerConfig } from '../../common/expressions'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index b018e62f1fd8f..3a28f137f93bd 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -22,27 +22,22 @@ import { LayoutDirection, } from '@elastic/charts'; import { PaletteOutput } from 'src/plugins/charts/public'; +import { calculateMinInterval, XYChart, XYChartRenderProps, xyChart } from './expression'; +import type { LensMultiTable } from '../../common'; import { - calculateMinInterval, - xyChart, - XYChart, - XYChartProps, - XYChartRenderProps, -} from './expression'; -import { LensMultiTable } from '../types'; -import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; -import React from 'react'; -import { shallow } from 'enzyme'; -import { + layerConfig, + legendConfig, + tickLabelsConfig, + gridlinesConfig, XYArgs, LegendConfig, - legendConfig, - layerConfig, LayerArgs, AxesSettingsConfig, - tickLabelsConfig, - gridlinesConfig, -} from './types'; + XYChartProps, +} from '../../common/expressions'; +import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; +import React from 'react'; +import { shallow } from 'enzyme'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { mountWithIntl } from '@kbn/test/jest'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index 7c767cd1d1b04..4cd63c5747e87 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -30,8 +30,7 @@ import { LabelOverflowConstraint, } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; -import { - ExpressionFunctionDefinition, +import type { ExpressionRenderDefinition, Datatable, DatatableRow, @@ -39,24 +38,20 @@ import { import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { RenderMode } from 'src/plugins/expressions'; -import { - LensMultiTable, - FormatFactory, - ILensInterpreterRenderHandlers, - LensFilterEvent, - LensBrushEvent, -} from '../types'; -import { XYArgs, SeriesType, visualizationTypes, LayerArgs } from './types'; +import type { ILensInterpreterRenderHandlers, LensFilterEvent, LensBrushEvent } from '../types'; +import type { LensMultiTable, FormatFactory } from '../../common'; +import { LayerArgs, SeriesType, XYChartProps } from '../../common/expressions'; +import { visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart, getSeriesColor } from './state_helpers'; -import { ExpressionValueSearchContext, search } from '../../../../../src/plugins/data/public'; +import { search } from '../../../../../src/plugins/data/public'; import { ChartsPluginSetup, PaletteRegistry, SeriesLayer, } from '../../../../../src/plugins/charts/public'; import { EmptyPlaceholder } from '../shared_components'; -import { fittingFunctionDefinitions, getFitOptions } from './fitting_functions'; +import { getFitOptions } from './fitting_functions'; import { getAxesConfiguration, GroupsConfiguration, validateExtent } from './axes_configuration'; import { getColorAssignments } from './color_assignment'; import { getXDomain, XyEndzones } from './x_domain'; @@ -76,16 +71,16 @@ type SeriesSpec = InferPropType & InferPropType & InferPropType; -export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; -} - -export interface XYRender { - type: 'render'; - as: 'lens_xy_chart_renderer'; - value: XYChartProps; -} +export { + legendConfig, + yAxisConfig, + tickLabelsConfig, + gridlinesConfig, + axisTitlesVisibilityConfig, + axisExtentConfig, + layerConfig, + xyChart, +} from '../../common/expressions'; export type XYChartRenderProps = XYChartProps & { chartsThemeService: ChartsPluginSetup['theme']; @@ -99,139 +94,6 @@ export type XYChartRenderProps = XYChartProps & { syncColors: boolean; }; -export const xyChart: ExpressionFunctionDefinition< - 'lens_xy_chart', - LensMultiTable | ExpressionValueSearchContext | null, - XYArgs, - XYRender -> = { - name: 'lens_xy_chart', - type: 'render', - inputTypes: ['lens_multitable', 'kibana_context', 'null'], - help: i18n.translate('xpack.lens.xyChart.help', { - defaultMessage: 'An X/Y chart', - }), - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - xTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - }, - yRightExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - }, - legend: { - types: ['lens_xy_legendConfig'], - help: i18n.translate('xpack.lens.xyChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...fittingFunctionDefinitions.map(({ id }) => id)], - help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - }, - valueLabels: { - types: ['string'], - options: ['hide', 'inside'], - help: '', - }, - tickLabelsVisibilitySettings: { - types: ['lens_xy_tickLabelsConfig'], - help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - gridlinesVisibilitySettings: { - types: ['lens_xy_gridlinesConfig'], - help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: ['lens_xy_axisTitlesVisibilityConfig'], - help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, - layers: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_layer'] as any, - help: 'Layers of visual series', - multi: true, - }, - curveType: { - types: ['string'], - options: ['LINEAR', 'CURVE_MONOTONE_X'], - help: i18n.translate('xpack.lens.xyChart.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - }, - fn(data: LensMultiTable, args: XYArgs) { - return { - type: 'render', - as: 'lens_xy_chart_renderer', - value: { - data, - args, - }, - }; - }, -}; - export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { const filteredLayers = getFilteredLayers(layers, data); if (filteredLayers.length === 0) return; diff --git a/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts b/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts index a1f8ad1fa259a..0b0878dfe9684 100644 --- a/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts @@ -6,57 +6,7 @@ */ import { Fit } from '@elastic/charts'; -import { i18n } from '@kbn/i18n'; - -export type FittingFunction = typeof fittingFunctionDefinitions[number]['id']; - -export const fittingFunctionDefinitions = [ - { - id: 'None', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { - defaultMessage: 'Hide', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', { - defaultMessage: 'Do not fill gaps', - }), - }, - { - id: 'Zero', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { - defaultMessage: 'Zero', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', { - defaultMessage: 'Fill gaps with zeros', - }), - }, - { - id: 'Linear', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { - defaultMessage: 'Linear', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', { - defaultMessage: 'Fill gaps with a line', - }), - }, - { - id: 'Carry', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { - defaultMessage: 'Last', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', { - defaultMessage: 'Fill gaps with the last value', - }), - }, - { - id: 'Lookahead', - title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { - defaultMessage: 'Next', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', { - defaultMessage: 'Fill gaps with the next value', - }), - }, -] as const; +import { FittingFunction } from '../../common/expressions'; export function getFitEnum(fittingFunction?: FittingFunction) { if (fittingFunction) { diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx index e4edfe918a242..e3489ae7808af 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx @@ -10,8 +10,8 @@ import { LegendActionProps, SeriesIdentifier } from '@elastic/charts'; import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test/jest'; import { ComponentType, ReactWrapper } from 'enzyme'; -import type { LayerArgs } from './types'; -import type { LensMultiTable } from '../types'; +import type { LensMultiTable } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; import { getLegendAction } from './get_legend_action'; import { LegendActionPopover } from '../shared_components'; diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx index c99bf948d6e37..0603328ee5bb3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx @@ -7,8 +7,9 @@ import React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; -import type { LayerArgs } from './types'; -import type { LensMultiTable, LensFilterEvent, FormatFactory } from '../types'; +import type { LensFilterEvent } from '../types'; +import type { LensMultiTable, FormatFactory } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; import { LegendActionPopover } from '../shared_components'; export const getLegendAction = ( diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index f29d0f9280246..de9ecd0b694e4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { CoreSetup } from 'kibana/public'; -import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { EditorFrameSetup, FormatFactory } from '../types'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; -import { LensPluginStartDependencies } from '../plugin'; +import type { CoreSetup } from 'kibana/public'; +import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; +import type { EditorFrameSetup } from '../types'; +import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import type { LensPluginStartDependencies } from '../plugin'; import { getTimeZone } from '../utils'; +import type { FormatFactory } from '../../common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index aa8dede62f566..e3b16f5981f88 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -6,8 +6,9 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { FramePublicAPI, DatasourcePublicAPI } from '../types'; -import { SeriesType, visualizationTypes, XYLayerConfig, YConfig, ValidLayer } from './types'; +import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; +import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common/expressions'; +import { visualizationTypes } from './types'; export function isHorizontalSeries(seriesType: SeriesType) { return ( diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index c89a5e81e35d0..b588cd5592a43 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -8,9 +8,10 @@ import { Ast } from '@kbn/interpreter/common'; import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; -import { State, ValidLayer, XYLayerConfig } from './types'; +import { State } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; +import { ValidLayer, XYLayerConfig } from '../../common/expressions'; export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => { const originalOrder = datasource diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 609622186af20..dcc147b4170a0 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { Position, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { PaletteOutput } from 'src/plugins/charts/public'; -import { ArgumentType, ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { LensIconChartArea } from '../assets/chart_area'; import { LensIconChartAreaStacked } from '../assets/chart_area_stacked'; import { LensIconChartAreaPercentage } from '../assets/chart_area_percentage'; @@ -21,499 +18,16 @@ import { LensIconChartBarHorizontalPercentage } from '../assets/chart_bar_horizo import { LensIconChartLine } from '../assets/chart_line'; import { VisualizationType } from '../types'; -import { FittingFunction } from './fitting_functions'; - -export interface LegendConfig { - /** - * Flag whether the legend should be shown. If there is just a single series, it will be hidden - */ - isVisible: boolean; - /** - * Position of the legend relative to the chart - */ - position: Position; - /** - * Flag whether the legend should be shown even with just a single series - */ - showSingleSeries?: boolean; - /** - * Flag whether the legend is inside the chart - */ - isInside?: boolean; - /** - * Horizontal Alignment of the legend when it is set inside chart - */ - horizontalAlignment?: HorizontalAlignment; - /** - * Vertical Alignment of the legend when it is set inside chart - */ - verticalAlignment?: VerticalAlignment; - /** - * Number of columns when legend is set inside chart - */ - floatingColumns?: number; -} - -type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' }; - -export const legendConfig: ExpressionFunctionDefinition< - 'lens_xy_legendConfig', - null, +import { + SeriesType, + ValueLabelConfig, LegendConfig, - LegendConfigResult -> = { - name: 'lens_xy_legendConfig', - aliases: [], - type: 'lens_xy_legendConfig', - help: `Configure the xy chart's legend`, - inputTypes: ['null'], - args: { - isVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isVisible.help', { - defaultMessage: 'Specifies whether or not the legend is visible.', - }), - }, - position: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('xpack.lens.xyChart.position.help', { - defaultMessage: 'Specifies the legend position.', - }), - }, - showSingleSeries: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { - defaultMessage: 'Specifies whether a legend with just a single entry should be shown', - }), - }, - isInside: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isInside.help', { - defaultMessage: 'Specifies whether a legend is inside the chart', - }), - }, - horizontalAlignment: { - types: ['string'], - options: [HorizontalAlignment.Right, HorizontalAlignment.Left], - help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { - defaultMessage: - 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', - }), - }, - verticalAlignment: { - types: ['string'], - options: [VerticalAlignment.Top, VerticalAlignment.Bottom], - help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { - defaultMessage: - 'Specifies the vertical alignment of the legend when it is displayed inside chart.', - }), - }, - floatingColumns: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { - defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', - }), - }, - }, - fn: function fn(input: unknown, args: LegendConfig) { - return { - type: 'lens_xy_legendConfig', - ...args, - }; - }, -}; - -export interface AxesSettingsConfig { - x: boolean; - yLeft: boolean; - yRight: boolean; -} - -type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; - -export const tickLabelsConfig: ExpressionFunctionDefinition< - 'lens_xy_tickLabelsConfig', - null, - AxesSettingsConfig, - TickLabelsConfigResult -> = { - name: 'lens_xy_tickLabelsConfig', - aliases: [], - type: 'lens_xy_tickLabelsConfig', - help: `Configure the xy chart's tick labels appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_tickLabelsConfig', - ...args, - }; - }, -}; - -type GridlinesConfigResult = AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; - -export const gridlinesConfig: ExpressionFunctionDefinition< - 'lens_xy_gridlinesConfig', - null, - AxesSettingsConfig, - GridlinesConfigResult -> = { - name: 'lens_xy_gridlinesConfig', - aliases: [], - type: 'lens_xy_gridlinesConfig', - help: `Configure the xy chart's gridlines appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_gridlinesConfig', - ...args, - }; - }, -}; - -type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { - type: 'lens_xy_axisTitlesVisibilityConfig'; -}; - -export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition< - 'lens_xy_axisTitlesVisibilityConfig', - null, - AxesSettingsConfig, - AxisTitlesVisibilityConfigResult -> = { - name: 'lens_xy_axisTitlesVisibilityConfig', - aliases: [], - type: 'lens_xy_axisTitlesVisibilityConfig', - help: `Configure the xy chart's axis titles appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_axisTitlesVisibilityConfig', - ...args, - }; - }, -}; - -export interface AxisExtentConfig { - mode: 'full' | 'dataBounds' | 'custom'; - lowerBound?: number; - upperBound?: number; -} - -export const axisExtentConfig: ExpressionFunctionDefinition< - 'lens_xy_axisExtentConfig', - null, AxisExtentConfig, - AxisExtentConfigResult -> = { - name: 'lens_xy_axisExtentConfig', - aliases: [], - type: 'lens_xy_axisExtentConfig', - help: `Configure the xy chart's axis extents`, - inputTypes: ['null'], - args: { - mode: { - types: ['string'], - options: ['full', 'dataBounds', 'custom'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - lowerBound: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - upperBound: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - }, - fn: function fn(input: unknown, args: AxisExtentConfig) { - return { - type: 'lens_xy_axisExtentConfig', - ...args, - }; - }, -}; - -export type AxisExtentConfigResult = AxisExtentConfig & { type: 'lens_xy_axisExtentConfig' }; - -interface AxisConfig { - title: string; - hide?: boolean; -} - -const axisConfig: { [key in keyof AxisConfig]: ArgumentType } = { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.title.help', { - defaultMessage: 'The axis title', - }), - }, - hide: { - types: ['boolean'], - default: false, - help: 'Show / hide axis', - }, -}; - -type YConfigResult = YConfig & { type: 'lens_xy_yConfig' }; - -export const yAxisConfig: ExpressionFunctionDefinition< - 'lens_xy_yConfig', - null, - YConfig, - YConfigResult -> = { - name: 'lens_xy_yConfig', - aliases: [], - type: 'lens_xy_yConfig', - help: `Configure the behavior of a xy chart's y axis metric`, - inputTypes: ['null'], - args: { - forAccessor: { - types: ['string'], - help: 'The accessor this configuration is for', - }, - axisMode: { - types: ['string'], - options: ['auto', 'left', 'right'], - help: 'The axis mode of the metric', - }, - color: { - types: ['string'], - help: 'The color of the series', - }, - }, - fn: function fn(input: unknown, args: YConfig) { - return { - type: 'lens_xy_yConfig', - ...args, - }; - }, -}; - -type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' }; - -export const layerConfig: ExpressionFunctionDefinition< - 'lens_xy_layer', - null, - LayerArgs, - LayerConfigResult -> = { - name: 'lens_xy_layer', - aliases: [], - type: 'lens_xy_layer', - help: `Configure a layer in the xy chart`, - inputTypes: ['null'], - args: { - ...axisConfig, - layerId: { - types: ['string'], - help: '', - }, - xAccessor: { - types: ['string'], - help: '', - }, - seriesType: { - types: ['string'], - options: [ - 'bar', - 'line', - 'area', - 'bar_stacked', - 'area_stacked', - 'bar_percentage_stacked', - 'area_percentage_stacked', - ], - help: 'The type of chart to display.', - }, - xScaleType: { - options: ['ordinal', 'linear', 'time'], - help: 'The scale type of the x axis', - default: 'ordinal', - }, - isHistogram: { - types: ['boolean'], - default: false, - help: 'Whether to layout the chart as a histogram', - }, - yScaleType: { - options: ['log', 'sqrt', 'linear', 'time'], - help: 'The scale type of the y axes', - default: 'linear', - }, - splitAccessor: { - types: ['string'], - help: 'The column to split by', - multi: false, - }, - accessors: { - types: ['string'], - help: 'The columns to display on the y axis.', - multi: true, - }, - yConfig: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_yConfig' as any], - help: 'Additional configuration for y axes', - multi: true, - }, - columnToLabel: { - types: ['string'], - help: 'JSON key-value pairs of column ID to label', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - }, - fn: function fn(input: unknown, args: LayerArgs) { - return { - type: 'lens_xy_layer', - ...args, - }; - }, -}; - -export type SeriesType = - | 'bar' - | 'bar_horizontal' - | 'line' - | 'area' - | 'bar_stacked' - | 'bar_percentage_stacked' - | 'bar_horizontal_stacked' - | 'bar_horizontal_percentage_stacked' - | 'area_stacked' - | 'area_percentage_stacked'; - -export type YAxisMode = 'auto' | 'left' | 'right'; - -export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; - -export interface YConfig { - forAccessor: string; - axisMode?: YAxisMode; - color?: string; -} - -export interface XYLayerConfig { - hide?: boolean; - layerId: string; - xAccessor?: string; - accessors: string[]; - yConfig?: YConfig[]; - seriesType: SeriesType; - splitAccessor?: string; - palette?: PaletteOutput; -} - -export interface ValidLayer extends XYLayerConfig { - xAccessor: NonNullable; -} - -export type LayerArgs = XYLayerConfig & { - columnToLabel?: string; // Actually a JSON key-value pair - yScaleType: 'time' | 'linear' | 'log' | 'sqrt'; - xScaleType: 'time' | 'linear' | 'ordinal'; - isHistogram: boolean; - // palette will always be set on the expression - palette: PaletteOutput; -}; - -// Arguments to XY chart expression, with computed properties -export interface XYArgs { - title?: string; - description?: string; - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; - legend: LegendConfig & { type: 'lens_xy_legendConfig' }; - valueLabels: ValueLabelConfig; - layers: LayerArgs[]; - fittingFunction?: FittingFunction; - axisTitlesVisibilitySettings?: AxesSettingsConfig & { - type: 'lens_xy_axisTitlesVisibilityConfig'; - }; - tickLabelsVisibilitySettings?: AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; - gridlinesVisibilitySettings?: AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; - curveType?: XYCurveType; - fillOpacity?: number; - hideEndzones?: boolean; - valuesInLegend?: boolean; -} - -export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X'; + XYLayerConfig, + XYCurveType, + AxesSettingsConfig, + FittingFunction, +} from '../../common/expressions'; // Persisted parts of the state export interface XYState { diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx index 1df7744524779..6080a8c68e57d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; -import { XYCurveType } from '../types'; +import type { XYCurveType } from '../../../common/expressions'; export interface LineCurveOptionProps { /** diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx index fb6ecec4d2801..3dba8757903e9 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui'; -import { FittingFunction, fittingFunctionDefinitions } from '../fitting_functions'; -import { ValueLabelConfig } from '../types'; +import { fittingFunctionDefinitions } from '../../../common/expressions'; +import type { FittingFunction, ValueLabelConfig } from '../../../common/expressions'; export interface MissingValuesOptionProps { valueLabels?: ValueLabelConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx index ec0c11a0b1d86..b4c8e8f40dde7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallowWithIntl as shallow } from '@kbn/test/jest'; import { Position } from '@elastic/charts'; -import { FramePublicAPI } from '../../types'; +import type { FramePublicAPI } from '../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { State } from '../types'; import { VisualOptionsPopover } from './visual_options_popover'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx index 843680e3f28ac..6d0e5c2d55b70 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx @@ -13,8 +13,8 @@ import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; import { XYState } from '../types'; import { hasHistogramSeries } from '../state_helpers'; -import { ValidLayer } from '../types'; -import { FramePublicAPI } from '../../types'; +import { ValidLayer } from '../../../common/expressions'; +import type { FramePublicAPI } from '../../types'; function getValueLabelDisableReason({ isAreaPercentage, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 304e323789c14..fd80b9d96d30a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -8,7 +8,8 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation } from '../types'; -import { State, SeriesType, XYLayerConfig } from './types'; +import type { State } from './types'; +import type { SeriesType, XYLayerConfig } from '../../common/expressions'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 199dccdf702f7..40caed7188190 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -15,14 +15,15 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { getSuggestions } from './xy_suggestions'; import { LayerContextMenu, XyToolbar, DimensionEditor } from './xy_config_panel'; -import { +import type { Visualization, OperationMetadata, VisualizationType, AccessorConfig, DatasourcePublicAPI, } from '../types'; -import { State, SeriesType, visualizationTypes, XYLayerConfig, XYState } from './types'; +import { State, visualizationTypes, XYState } from './types'; +import type { SeriesType, XYLayerConfig } from '../../common/expressions'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; import { LensIconChartBarStacked } from '../assets/chart_bar_stacked'; diff --git a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx b/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx index 369063644a754..ccb047d54e369 100644 --- a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx @@ -8,8 +8,8 @@ import { uniq } from 'lodash'; import React from 'react'; import { Endzones } from '../../../../../src/plugins/charts/public'; -import { LensMultiTable } from '../types'; -import { LayerArgs } from './types'; +import type { LensMultiTable } from '../../common'; +import type { LayerArgs } from '../../common/expressions'; export interface XDomain { min?: number; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx index 3128527334553..061d0cbc4b4b5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx @@ -21,23 +21,21 @@ import { EuiToolTip, EuiIcon, } from '@elastic/eui'; -import { PaletteRegistry } from 'src/plugins/charts/public'; -import { +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { VisualizationLayerWidgetProps, VisualizationToolbarProps, VisualizationDimensionEditorProps, - FormatFactory, FramePublicAPI, } from '../types'; -import { - State, +import { State, visualizationTypes, XYState } from './types'; +import type { FormatFactory } from '../../common'; +import type { SeriesType, - visualizationTypes, YAxisMode, AxesSettingsConfig, AxisExtentConfig, - XYState, -} from './types'; +} from '../../common/expressions'; import { isHorizontalChart, isHorizontalSeries, getSeriesColor } from './state_helpers'; import { trackUiEvent } from '../lens_ui_telemetry'; import { LegendSettingsPopover } from '../shared_components'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index a494d51f51681..893bd5fd04ee4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -16,7 +16,8 @@ import { TableSuggestion, TableChangeType, } from '../types'; -import { State, SeriesType, XYState, visualizationTypes, XYLayerConfig } from './types'; +import { State, XYState, visualizationTypes } from './types'; +import type { SeriesType, XYLayerConfig } from '../../common/expressions'; import { getIconForSeries } from './state_helpers'; const columnSortOrder = { diff --git a/x-pack/plugins/lens/server/plugin.tsx b/x-pack/plugins/lens/server/plugin.tsx index c23c98cd12aec..b47019fa54ec0 100644 --- a/x-pack/plugins/lens/server/plugin.tsx +++ b/x-pack/plugins/lens/server/plugin.tsx @@ -9,6 +9,7 @@ import { Plugin, CoreSetup, CoreStart, PluginInitializerContext, Logger } from ' import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { Observable } from 'rxjs'; import { PluginStart as DataPluginStart } from 'src/plugins/data/server'; +import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; import { setupRoutes } from './routes'; import { @@ -24,6 +25,7 @@ export interface PluginSetupContract { usageCollection?: UsageCollectionSetup; taskManager?: TaskManagerSetupContract; embeddable: EmbeddableSetup; + expressions: ExpressionsServerSetup; } export interface PluginStartContract { diff --git a/x-pack/plugins/security/server/routes/authentication/saml.test.ts b/x-pack/plugins/security/server/routes/authentication/saml.test.ts index d27ea5f6dca71..2ed6727492768 100644 --- a/x-pack/plugins/security/server/routes/authentication/saml.test.ts +++ b/x-pack/plugins/security/server/routes/authentication/saml.test.ts @@ -43,6 +43,15 @@ describe('SAML authentication routes', () => { routeHandler = acsRouteHandler; }); + it('additionally registers BWC route', () => { + expect( + router.post.mock.calls.find(([{ path }]) => path === '/api/security/saml/callback') + ).toBeDefined(); + expect( + router.post.mock.calls.find(([{ path }]) => path === '/api/security/v1/saml') + ).toBeDefined(); + }); + it('correctly defines route.', () => { expect(routeConfig.options).toEqual({ authRequired: false, diff --git a/x-pack/plugins/security/server/routes/authentication/saml.ts b/x-pack/plugins/security/server/routes/authentication/saml.ts index 8a343c2413779..11baa10c342e1 100644 --- a/x-pack/plugins/security/server/routes/authentication/saml.ts +++ b/x-pack/plugins/security/server/routes/authentication/saml.ts @@ -14,40 +14,56 @@ import { ROUTE_TAG_AUTH_FLOW, ROUTE_TAG_CAN_REDIRECT } from '../tags'; /** * Defines routes required for SAML authentication. */ -export function defineSAMLRoutes({ router, getAuthenticationService }: RouteDefinitionParams) { - router.post( - { - path: '/api/security/saml/callback', - validate: { - body: schema.object( - { SAMLResponse: schema.string(), RelayState: schema.maybe(schema.string()) }, - { unknowns: 'ignore' } - ), - }, - options: { - authRequired: false, - xsrfRequired: false, - tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], - }, - }, - async (context, request, response) => { - // When authenticating using SAML we _expect_ to redirect to the Kibana target location. - const authenticationResult = await getAuthenticationService().login(request, { - provider: { type: SAMLAuthenticationProvider.type }, - value: { - type: SAMLLogin.LoginWithSAMLResponse, - samlResponse: request.body.SAMLResponse, - relayState: request.body.RelayState, +export function defineSAMLRoutes({ + router, + getAuthenticationService, + basePath, + logger, +}: RouteDefinitionParams) { + // Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used. + for (const path of ['/api/security/saml/callback', '/api/security/v1/saml']) { + router.post( + { + path, + validate: { + body: schema.object( + { SAMLResponse: schema.string(), RelayState: schema.maybe(schema.string()) }, + { unknowns: 'ignore' } + ), }, - }); + options: { + authRequired: false, + xsrfRequired: false, + tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], + }, + }, + async (context, request, response) => { + if (path === '/api/security/v1/saml') { + const serverBasePath = basePath.serverBasePath; + logger.warn( + // When authenticating using SAML we _expect_ to redirect to the SAML Identity provider. + `The "${serverBasePath}${path}" URL is deprecated and might stop working in a future release. Please use "${serverBasePath}/api/security/saml/callback" URL instead.` + ); + } - if (authenticationResult.redirected()) { - return response.redirected({ - headers: { location: authenticationResult.redirectURL! }, + // When authenticating using SAML we _expect_ to redirect to the Kibana target location. + const authenticationResult = await getAuthenticationService().login(request, { + provider: { type: SAMLAuthenticationProvider.type }, + value: { + type: SAMLLogin.LoginWithSAMLResponse, + samlResponse: request.body.SAMLResponse, + relayState: request.body.RelayState, + }, }); - } - return response.unauthorized({ body: authenticationResult.error }); - } - ); + if (authenticationResult.redirected()) { + return response.redirected({ + headers: { location: authenticationResult.redirectURL! }, + }); + } + + return response.unauthorized({ body: authenticationResult.error }); + } + ); + } } diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 871f1a01e3de0..c679828a1c494 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -215,7 +215,7 @@ const nestedDeepLinks: SecurityDeepLinks = { title: i18n.translate('xpack.securitySolution.search.hosts.externalAlerts', { defaultMessage: 'External Alerts', }), - path: `${HOSTS_PATH}/alerts`, + path: `${HOSTS_PATH}/externalAlerts`, }, ], premium: [ diff --git a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts index 148deda9aec76..05e1c2c4dca81 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts @@ -26,7 +26,7 @@ type EventHandlerCallback = MouseEventHandlerSee policies */ export const useNavigateToAppEventHandler = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts index ca14dde18455b..e8fa53e2cf920 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts @@ -14,6 +14,7 @@ import { MANAGEMENT_STORE_GLOBAL_NAMESPACE, } from '../../../../common/constants'; import { useAppUrl } from '../../../../../common/lib/kibana'; +import { pagePathGetters } from '../../../../../../../fleet/public'; export function useEndpointSelector(selector: (state: EndpointState) => TSelected) { return useSelector(function (state: State) { @@ -47,7 +48,8 @@ export const useAgentDetailsIngestUrl = ( ): { url: string; appId: string; appPath: string } => { const { getAppUrl } = useAppUrl(); return useMemo(() => { - const appPath = `#/fleet/agents/${agentId}/activity`; + const appPath = pagePathGetters.agent_details_logs({ agentId })[1]; + return { url: `${getAppUrl({ appId: 'fleet' })}${appPath}`, appId: 'fleet', diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 584e6df1ff781..03df5d2bcbac7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -120,13 +120,13 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentPolicyLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.policy_details({ policyId: fleetAgentPolicies[endpointPolicyId], })[1] }`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.policy_details({ policyId: fleetAgentPolicies[endpointPolicyId], })[1] @@ -145,13 +145,13 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentDetailsLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] }`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] @@ -169,17 +169,17 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentPolicyReassignLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] - }/activity?openReassignFlyout=true`, + }?openReassignFlyout=true`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] - }/activity?openReassignFlyout=true`, + }?openReassignFlyout=true`, children: ( { }); it('navigates to the Ingest Agent Policy page', async () => { const agentPolicyLink = await renderResult.findByTestId('agentPolicyLink'); - expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet#/policies/${agentPolicyId}`); + expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet/policies/${agentPolicyId}`); }); it('navigates to the Ingest Agent Details page', async () => { const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink'); - expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet#/agents/${agentId}`); + expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet/agents/${agentId}`); }); it('navigates to the Ingest Agent Details page with policy reassign', async () => { const agentPolicyReassignLink = await renderResult.findByTestId('agentPolicyReassignLink'); expect(agentPolicyReassignLink.getAttribute('href')).toEqual( - `/app/fleet#/agents/${agentId}/activity?openReassignFlyout=true` + `/app/fleet/agents/${agentId}?openReassignFlyout=true` ); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index c78d4ca6af634..74f5b15a72727 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -156,7 +156,7 @@ export const EndpointList = () => { const handleCreatePolicyClick = useNavigateToAppEventHandler( 'fleet', { - path: `#/integrations/${ + path: `/integrations/${ endpointPackageVersion ? `/endpoint-${endpointPackageVersion}` : '' }/add-integration`, state: { @@ -203,7 +203,7 @@ export const EndpointList = () => { const handleDeployEndpointsClick = useNavigateToAppEventHandler( 'fleet', { - path: `#/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, + path: `/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, state: { onDoneNavigateTo: [ 'securitySolution', diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx index 7af9f84ad0875..2d21ec9565476 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx @@ -63,7 +63,7 @@ describe('OverviewEmpty', () => { fill: false, label: 'Add Endpoint Security', onClick: undefined, - url: `#/integrations/endpoint-${endpointPackageVersion}/add-integration`, + url: `/integrations/endpoint-${endpointPackageVersion}/add-integration`, }, }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx index c75438e18f5d5..6f885b348cdeb 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx @@ -36,7 +36,7 @@ const OverviewEmptyComponent: React.FC = () => { const endpointIntegrationUrlPath = endpointPackageVersion ? `/endpoint-${endpointPackageVersion}/add-integration` : ''; - const endpointIntegrationUrl = `#/integrations${endpointIntegrationUrlPath}`; + const endpointIntegrationUrl = `/integrations${endpointIntegrationUrlPath}`; const handleEndpointClick = useNavigateToAppEventHandler('fleet', { path: endpointIntegrationUrl, }); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx index e511c76603cda..839d16b3e3e36 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/breadcrumbs.tsx @@ -7,10 +7,9 @@ /* eslint-disable react/display-name */ -import { i18n } from '@kbn/i18n'; -import { EuiBreadcrumb, EuiBetaBadge } from '@elastic/eui'; +import { EuiBreadcrumb } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; -import { BetaHeader, ThemedBreadcrumbs } from './styles'; +import { ThemedBreadcrumbs } from './styles'; import { useColors } from '../use_colors'; /** @@ -31,16 +30,6 @@ export const Breadcrumbs = memo(function ({ breadcrumbs }: { breadcrumbs: EuiBre const { resolverBreadcrumbBackground, resolverEdgeText } = useColors(); return ( <> - - - - - - - -`; diff --git a/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx index c7884683e1aed..ca6be33d63ebb 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx @@ -94,8 +94,14 @@ export const DurationChartComponent = ({ const monitor = useSelector(monitorStatusSelector); return ( - - {hasLines ? ( + + {hasLines && typeof monitor?.monitor?.type === 'string' ? ( getTickFormat(d)} title={i18n.translate('xpack.uptime.monitorCharts.durationChart.leftAxis.title', { defaultMessage: 'Duration in {unit}', - values: { unit: monitor?.monitor.type === 'browser' ? SECONDS_LABEL : MS_LABEL }, + values: { unit: monitor.monitor.type === 'browser' ? SECONDS_LABEL : MS_LABEL }, })} labelFormat={(d) => monitor?.monitor.type === 'browser' ? `${microToSec(d)}` : `${microToMilli(d)}` @@ -127,7 +133,7 @@ export const DurationChartComponent = ({ /> diff --git a/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx index d7ae92a0e7654..7669ee29786f3 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/duration_charts.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import DateMath from '@elastic/datemath'; import { DurationChartComponent } from './duration_chart'; import { MonitorDurationResult } from '../../../../common/types'; -import { shallowWithRouter } from '../../../lib'; +import { render } from '../../../lib/helper/rtl_helpers'; describe('MonitorCharts component', () => { let dateMathSpy: any; @@ -48,13 +48,37 @@ describe('MonitorCharts component', () => { }; it('renders the component without errors', () => { - const component = shallowWithRouter( + const { getByLabelText } = render( + />, + { + state: { + monitorStatus: { + loading: false, + status: { + docId: 'docId', + timestamp: '123', + monitor: { + duration: { us: 123 }, + id: 'mon-id', + status: 'up', + type: 'tcp', + }, + }, + }, + }, + } + ); + expect(getByLabelText(`A chart displaying the monitor's ping duration, grouped by location.`)); + }); + + it('renders an empty state when no monitor data is present', () => { + const { getByText } = render( + ); - expect(component).toMatchSnapshot(); + expect(getByText('No duration data available')); }); }); diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts index 3cf1c7f787840..c6a75eccfa4c8 100644 --- a/x-pack/test/api_integration/apis/ml/modules/index.ts +++ b/x-pack/test/api_integration/apis/ml/modules/index.ts @@ -8,6 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); const ml = getService('ml'); const supertest = getService('supertest'); @@ -15,8 +16,12 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { describe('modules', function () { before(async () => { + // use empty_kibana to make sure the fleet setup is removed correctly after the tests + await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); + // Fleet need to be setup to be able to setup packages await supertest.post(`/api/fleet/setup`).set({ 'kbn-xsrf': 'some-xsrf-token' }).expect(200); + for (const fleetPackage of fleetPackages) { await ml.testResources.installFleetPackage(fleetPackage); } @@ -26,6 +31,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { for (const fleetPackage of fleetPackages) { await ml.testResources.removeFleetPackage(fleetPackage); } + await esArchiver.unload('x-pack/test/functional/es_archives/empty_kibana'); }); loadTestFile(require.resolve('./get_module')); diff --git a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts index bc06b72993630..616402098acec 100644 --- a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts +++ b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts @@ -123,7 +123,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'apmCorrelationsLatencyCorrelationsChartTitle' ); expect(apmCorrelationsLatencyCorrelationsChartTitle).to.be( - `Latency distribution for ${testData.serviceName}` + `Latency distribution for ${testData.serviceName} (Log-Log Plot)` ); await testSubjects.existOrFail('apmCorrelationsChart', { timeout: 10000, diff --git a/x-pack/test/functional/apps/dashboard/index.ts b/x-pack/test/functional/apps/dashboard/index.ts index 99f8c6ffedefc..d5aded01fce7b 100644 --- a/x-pack/test/functional/apps/dashboard/index.ts +++ b/x-pack/test/functional/apps/dashboard/index.ts @@ -21,5 +21,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./dashboard_maps_by_value')); loadTestFile(require.resolve('./migration_smoke_tests/lens_migration_smoke_test')); + loadTestFile(require.resolve('./migration_smoke_tests/tsvb_migration_smoke_test')); }); } diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson b/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson new file mode 100644 index 0000000000000..d37184717fd02 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_12_1.ndjson @@ -0,0 +1,3 @@ +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"64d009bf-b2cf-44ab-bfda-06cf883f2a24\"},\"panelIndex\":\"64d009bf-b2cf-44ab-bfda-06cf883f2a24\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"TSVB Dashboard\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"split_color_mode\":\"kibana\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\"}],\"time_field\":\"\",\"index_pattern\":\"\",\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{}}},\"hidePanelTitles\":false,\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"e7cb1014-a092-44e8-a4d7-12fb6ad2bb7f\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"Time Series Drilldown\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}},{\"eventId\":\"b5eebcad-8eb3-4654-bab4-785848686100\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"Drilldown 2\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}},\"title\":\"Time Series\"},{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"0a7a9a89-7d4b-4a53-97f2-7d30f6670450\"},\"panelIndex\":\"0a7a9a89-7d4b-4a53-97f2-7d30f6670450\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"metric\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"split_color_mode\":\"kibana\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"memory\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"label\":\"\"}],\"time_field\":\"\",\"index_pattern\":\"\",\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"default_index_pattern\":\"logstash*\",\"default_timefield\":\"@timestamp\",\"isModelInvalid\":false,\"background_color_rules\":[{\"id\":\"e35e8210-e3f0-11eb-bc03-1f348ce358cc\"}],\"time_range_mode\":\"entire_time_range\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"dfb6a2b8-04a3-4694-b144-7d6105c9f5ad\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"Drilldown\",\"config\":{\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}},\"title\":\"Average Memory\"},{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"4ed295aa-b56e-4b3c-b749-89b21b3db498\"},\"panelIndex\":\"4ed295aa-b56e-4b3c-b749-89b21b3db498\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"top_n\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"terms\",\"split_color_mode\":\"kibana\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\"}],\"time_field\":\"\",\"index_pattern\":\"\",\"interval\":\"1d\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"default_index_pattern\":\"logstash*\",\"default_timefield\":\"@timestamp\",\"isModelInvalid\":false,\"bar_color_rules\":[{\"id\":\"506f5870-e3f1-11eb-bc03-1f348ce358cc\"}]},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"Top N\"},{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":24,\"h\":15,\"i\":\"10354fb6-de13-4331-92e9-5d60e0782ef6\"},\"panelIndex\":\"10354fb6-de13-4331-92e9-5d60e0782ef6\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"gauge\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"split_color_mode\":\"kibana\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\"}],\"time_field\":\"\",\"index_pattern\":\"\",\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"default_index_pattern\":\"logstash*\",\"default_timefield\":\"@timestamp\",\"isModelInvalid\":false,\"gauge_color_rules\":[{\"id\":\"a521c790-e3f1-11eb-bc03-1f348ce358cc\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"Guage\"},{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"c21ec51b-ba7d-4fe3-95ed-63c198852368\"},\"panelIndex\":\"c21ec51b-ba7d-4fe3-95ed-63c198852368\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"markdown\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"split_color_mode\":\"kibana\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\"}],\"time_field\":\"\",\"index_pattern\":\"\",\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"default_index_pattern\":\"logstash*\",\"default_timefield\":\"@timestamp\",\"isModelInvalid\":false,\"markdown\":\"The count was {{ count.last.raw }}\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"TSVB Markdown\"},{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"1890c642-9782-43e0-851c-5ffb3775a67d\"},\"panelIndex\":\"1890c642-9782-43e0-851c-5ffb3775a67d\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"table\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"split_color_mode\":\"kibana\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\"}],\"time_field\":\"\",\"index_pattern\":\"\",\"interval\":\"1d\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"default_index_pattern\":\"logstash*\",\"default_timefield\":\"@timestamp\",\"isModelInvalid\":false,\"bar_color_rules\":[{\"id\":\"d1f993b0-e3f1-11eb-bc03-1f348ce358cc\"}],\"pivot_id\":\"clientip\",\"pivot_type\":\"ip\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"TSVB Table\"},{\"version\":\"7.12.1\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"da27208b-2425-41b7-b02a-ec456f7ef55e\"},\"panelIndex\":\"da27208b-2425-41b7-b02a-ec456f7ef55e\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"annotations\":[{\"fields\":\"bytes\",\"template\":\"Bytes {{bytes}}\",\"index_pattern\":\"logstash*\",\"query_string\":{\"query\":\"bytes > 18000\",\"language\":\"kuery\"},\"color\":\"#F00\",\"hidden\":false,\"icon\":\"fa-tag\",\"id\":\"f2f4e990-e4c1-11eb-b885-218b39edaf39\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1,\"time_field\":\"@timestamp\"}],\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"id\":\"35d9b2e0-e4c2-11eb-b885-218b39edaf39\"}],\"default_index_pattern\":\"logstash*\",\"default_timefield\":\"@timestamp\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"\",\"line_width\":1,\"metrics\":[{\"field\":\"bytes\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"max\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"kibana\",\"split_mode\":\"everything\",\"stacked\":\"none\",\"type\":\"timeseries\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"TSVB w Annotations\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-21T10:30:00.000Z","timeRestore":true,"timeTo":"2015-09-22T12:00:00.000Z","title":"TSVB Index Pattern Smoke Test","version":1},"coreMigrationVersion":"7.12.1","id":"226f4380-e3f2-11eb-a821-bb6e72afa109","migrationVersion":{"dashboard":"7.11.0"},"references":[{"id":"226f4380-e3f2-11eb-a821-bb6e72afa109","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:e7cb1014-a092-44e8-a4d7-12fb6ad2bb7f:dashboardId","type":"dashboard"},{"id":"226f4380-e3f2-11eb-a821-bb6e72afa109","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:b5eebcad-8eb3-4654-bab4-785848686100:dashboardId","type":"dashboard"},{"id":"226f4380-e3f2-11eb-a821-bb6e72afa109","name":"drilldown:DASHBOARD_TO_DASHBOARD_DRILLDOWN:dfb6a2b8-04a3-4694-b144-7d6105c9f5ad:dashboardId","type":"dashboard"}],"type":"dashboard","updated_at":"2021-07-14T16:46:33.549Z","version":"WzY0MiwzXQ=="} +{"attributes":{"fieldAttrs":"{}","fields":"[]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"logstash*"},"coreMigrationVersion":"7.12.1","id":"d0d5bf10-e3ef-11eb-a821-bb6e72afa109","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"type":"index-pattern","updated_at":"2021-07-13T15:34:30.529Z","version":"WzMwOSwzXQ=="} +{"exportedCount":2,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson b/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson new file mode 100644 index 0000000000000..1a8b1b7f5391c --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/migration_smoke_tests/exports/tsvb_dashboard_migration_test_7_13_3.ndjson @@ -0,0 +1,3 @@ +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.13.3\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"90f0f762-f2ae-4400-b650-e699513c6dbf\"},\"panelIndex\":\"90f0f762-f2ae-4400-b650-e699513c6dbf\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"Non Kibana Index\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash*\",\"use_kibana_indexes\":false,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"isModelInvalid\":false},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"enhancements\":{}}},{\"version\":\"7.13.3\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"e7112256-80ef-4d79-836c-6480e0039179\"},\"panelIndex\":\"e7112256-80ef-4d79-836c-6480e0039179\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metrics\",\"params\":{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\"}],\"time_field\":\"\",\"use_kibana_indexes\":true,\"interval\":\"\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"tooltip_mode\":\"show_all\",\"isModelInvalid\":false,\"index_pattern_ref_name\":\"metrics_e7112256-80ef-4d79-836c-6480e0039179_0_index_pattern\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}}},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"Kibana Index\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"2015-09-21T10:30:00.000Z","timeRestore":true,"timeTo":"2015-09-22T12:00:00.000Z","title":"TSVB 7.13.3","version":1},"coreMigrationVersion":"7.13.3","id":"d8c09ac0-e4ca-11eb-8015-4bb2e4229ebd","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"d0d5bf10-e3ef-11eb-a821-bb6e72afa109","name":"e7112256-80ef-4d79-836c-6480e0039179:metrics_e7112256-80ef-4d79-836c-6480e0039179_0_index_pattern","type":"index_pattern"}],"type":"dashboard","updated_at":"2021-07-23T18:45:00.781Z","version":"Wzc1NCwxXQ=="} +{"attributes":{"fieldAttrs":"{}","fields":"[]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"logstash*"},"coreMigrationVersion":"7.13.3","id":"d0d5bf10-e3ef-11eb-a821-bb6e72afa109","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"type":"index-pattern","updated_at":"2021-07-23T18:43:39.839Z","version":"WzcyMiwxXQ=="} +{"exportedCount":2,"missingRefCount":1,"missingReferences":[{"id":"d0d5bf10-e3ef-11eb-a821-bb6e72afa109","type":"index_pattern"}]} \ No newline at end of file diff --git a/x-pack/test/functional/apps/dashboard/migration_smoke_tests/tsvb_migration_smoke_test.ts b/x-pack/test/functional/apps/dashboard/migration_smoke_tests/tsvb_migration_smoke_test.ts new file mode 100644 index 0000000000000..22606cf6d2862 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/migration_smoke_tests/tsvb_migration_smoke_test.ts @@ -0,0 +1,132 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import path from 'path'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const testSubjects = getService('testSubjects'); + const dashboardPanelActions = getService('dashboardPanelActions'); + + const PageObjects = getPageObjects(['common', 'settings', 'header', 'savedObjects', 'dashboard']); + + describe('Export import saved objects between versions', () => { + describe('From 7.12.1', () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.uiSettings.replace({}); + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + await PageObjects.savedObjects.importFile( + path.join(__dirname, 'exports', 'tsvb_dashboard_migration_test_7_12_1.ndjson') + ); + }); + + it('should be able to import dashboard with TSVB panels from 7.12.1', async () => { + // this will catch cases where there is an error in the migrations. + await PageObjects.savedObjects.checkImportSucceeded(); + await PageObjects.savedObjects.clickImportDone(); + }); + + it('should render all panels on the dashboard', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('TSVB Index Pattern Smoke Test'); + + // dashboard should load properly + await PageObjects.dashboard.expectOnDashboard('TSVB Index Pattern Smoke Test'); + await PageObjects.dashboard.waitForRenderComplete(); + + // There should be 0 error embeddables on the dashboard + const errorEmbeddables = await testSubjects.findAll('embeddableStackError'); + expect(errorEmbeddables.length).to.be(0); + }); + + it('should show the edit action for all panels', async () => { + await PageObjects.dashboard.switchToEditMode(); + + // All panels should be editable. This will catch cases where an error does not create an error embeddable. + const panelTitles = await PageObjects.dashboard.getPanelTitles(); + for (const title of panelTitles) { + await dashboardPanelActions.expectExistsEditPanelAction(title); + } + }); + + it('should retain all panel drilldowns from 7.12.1', async () => { + // Both panels configured with drilldowns in 7.12.1 should still have drilldowns. + const totalPanels = await PageObjects.dashboard.getPanelCount(); + let panelsWithDrilldowns = 0; + let drilldownCount = 0; + for (let panelIndex = 0; panelIndex < totalPanels; panelIndex++) { + const panelDrilldownCount = await PageObjects.dashboard.getPanelDrilldownCount( + panelIndex + ); + if (panelDrilldownCount >= 1) { + panelsWithDrilldowns++; + } + + drilldownCount += await PageObjects.dashboard.getPanelDrilldownCount(panelIndex); + } + expect(panelsWithDrilldowns).to.be(2); + expect(drilldownCount).to.be(3); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); + await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); + }); + }); + + describe('from 7.13.3', () => { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); + await kibanaServer.uiSettings.replace({}); + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + await PageObjects.savedObjects.importFile( + path.join(__dirname, 'exports', 'tsvb_dashboard_migration_test_7_13_3.ndjson') + ); + }); + + it('should be able to import dashboard with TSVB panels from 7.13.3', async () => { + // this will catch cases where there is an error in the migrations. + await PageObjects.savedObjects.checkImportSucceeded(); + await PageObjects.savedObjects.clickImportDone(); + }); + + it('should render all panels on the dashboard', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('TSVB 7.13.3'); + + // dashboard should load properly + await PageObjects.dashboard.expectOnDashboard('TSVB 7.13.3'); + await PageObjects.dashboard.waitForRenderComplete(); + + // There should be 0 error embeddables on the dashboard + const errorEmbeddables = await testSubjects.findAll('embeddableStackError'); + expect(errorEmbeddables.length).to.be(0); + }); + + it('should show the edit action for all panels', async () => { + await PageObjects.dashboard.switchToEditMode(); + + // All panels should be editable. This will catch cases where an error does not create an error embeddable. + const panelTitles = await PageObjects.dashboard.getPanelTitles(); + for (const title of panelTitles) { + await dashboardPanelActions.expectExistsEditPanelAction(title); + } + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); + await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); + }); + }); + }); +} diff --git a/x-pack/test/functional/page_objects/synthetics_integration_page.ts b/x-pack/test/functional/page_objects/synthetics_integration_page.ts index 3321234a345e4..81ddaf06febd9 100644 --- a/x-pack/test/functional/page_objects/synthetics_integration_page.ts +++ b/x-pack/test/functional/page_objects/synthetics_integration_page.ts @@ -24,25 +24,17 @@ export function SyntheticsIntegrationPageProvider({ * */ async navigateToPackagePage(packageVersion: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'fleet', - `/integrations/synthetics-${packageVersion}/add-integration`, - { - shouldUseHashForSubUrl: true, - useActualUrl: true, - } + `/integrations/synthetics-${packageVersion}/add-integration` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, async navigateToPackageEditPage(packageId: string, agentId: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'fleet', - `/policies/${agentId}/edit-integration/${packageId}`, - { - shouldUseHashForSubUrl: true, - useActualUrl: true, - } + `/policies/${agentId}/edit-integration/${packageId}` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, diff --git a/x-pack/test/functional/services/ml/anomaly_explorer.ts b/x-pack/test/functional/services/ml/anomaly_explorer.ts index 6ec45204388d7..4392de7ee5567 100644 --- a/x-pack/test/functional/services/ml/anomaly_explorer.ts +++ b/x-pack/test/functional/services/ml/anomaly_explorer.ts @@ -10,6 +10,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export function MachineLearningAnomalyExplorerProvider({ getService }: FtrProviderContext) { + const retry = getService('retry'); const testSubjects = getService('testSubjects'); return { @@ -82,12 +83,8 @@ export function MachineLearningAnomalyExplorerProvider({ getService }: FtrProvid }, async addAndEditSwimlaneInDashboard(dashboardTitle: string) { - await this.filterWithSearchString(dashboardTitle); - await testSubjects.isDisplayed('mlDashboardSelectionTable > checkboxSelectAll'); - await testSubjects.clickWhenNotDisabled('mlDashboardSelectionTable > checkboxSelectAll'); - expect(await testSubjects.isChecked('mlDashboardSelectionTable > checkboxSelectAll')).to.be( - true - ); + await this.filterDashboardSearchWithSearchString(dashboardTitle); + await this.selectAllDashboards(); await testSubjects.clickWhenNotDisabled('mlAddAndEditDashboardButton'); // changing to the dashboard app might take sime time const embeddable = await testSubjects.find('mlAnomalySwimlaneEmbeddableWrapper', 30 * 1000); @@ -106,11 +103,30 @@ export function MachineLearningAnomalyExplorerProvider({ getService }: FtrProvid await testSubjects.existOrFail('~mlDashboardSelectionTable', { timeout: 60 * 1000 }); }, - async filterWithSearchString(filter: string) { + async filterDashboardSearchWithSearchString(filter: string) { await this.waitForDashboardsToLoad(); const searchBarInput = await testSubjects.find('mlDashboardsSearchBox'); await searchBarInput.clearValueWithKeyboard(); await searchBarInput.type(filter); + await this.assertDashboardSearchInputValue(filter); + }, + + async assertDashboardSearchInputValue(expectedSearchValue: string) { + const searchBarInput = await testSubjects.find('mlDashboardsSearchBox'); + const actualSearchValue = await searchBarInput.getAttribute('value'); + expect(actualSearchValue).to.eql( + expectedSearchValue, + `Dashboard search input value should be '${expectedSearchValue}' (got '${actualSearchValue}')` + ); + }, + + async selectAllDashboards() { + await retry.tryForTime(3000, async () => { + await testSubjects.clickWhenNotDisabled('mlDashboardSelectionTable > checkboxSelectAll'); + expect( + await testSubjects.isChecked('mlDashboardSelectionTable > checkboxSelectAll') + ).to.eql(true, 'Checkbox to select all dashboards should be selected'); + }); }, async assertClearSelectionButtonVisible(expectVisible: boolean) { diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index b22748608589e..ab3692be9288f 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -404,42 +404,58 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async assertConfigurationStepActive() { - await testSubjects.existOrFail('mlAnalyticsCreateJobWizardConfigurationStep active'); + await testSubjects.existOrFail('mlAnalyticsCreateJobWizardConfigurationStep active', { + timeout: 3000, + }); }, async assertAdditionalOptionsStepActive() { - await testSubjects.existOrFail('mlAnalyticsCreateJobWizardAdvancedStep active'); + await testSubjects.existOrFail('mlAnalyticsCreateJobWizardAdvancedStep active', { + timeout: 3000, + }); }, async assertDetailsStepActive() { - await testSubjects.existOrFail('mlAnalyticsCreateJobWizardDetailsStep active'); + await testSubjects.existOrFail('mlAnalyticsCreateJobWizardDetailsStep active', { + timeout: 3000, + }); }, async assertCreateStepActive() { - await testSubjects.existOrFail('mlAnalyticsCreateJobWizardCreateStep active'); + await testSubjects.existOrFail('mlAnalyticsCreateJobWizardCreateStep active', { + timeout: 3000, + }); }, async assertValidationStepActive() { - await testSubjects.existOrFail('mlAnalyticsCreateJobWizardValidationStepWrapper active'); + await testSubjects.existOrFail('mlAnalyticsCreateJobWizardValidationStepWrapper active', { + timeout: 3000, + }); }, async continueToAdditionalOptionsStep() { - await retry.tryForTime(5000, async () => { - await testSubjects.clickWhenNotDisabled('mlAnalyticsCreateJobWizardContinueButton'); + await retry.tryForTime(15 * 1000, async () => { + await testSubjects.clickWhenNotDisabled( + 'mlAnalyticsCreateJobWizardConfigurationStep active > mlAnalyticsCreateJobWizardContinueButton' + ); await this.assertAdditionalOptionsStepActive(); }); }, async continueToDetailsStep() { - await retry.tryForTime(5000, async () => { - await testSubjects.clickWhenNotDisabled('mlAnalyticsCreateJobWizardContinueButton'); + await retry.tryForTime(15 * 1000, async () => { + await testSubjects.clickWhenNotDisabled( + 'mlAnalyticsCreateJobWizardAdvancedStep active > mlAnalyticsCreateJobWizardContinueButton' + ); await this.assertDetailsStepActive(); }); }, async continueToValidationStep() { - await retry.tryForTime(5000, async () => { - await testSubjects.clickWhenNotDisabled('mlAnalyticsCreateJobWizardContinueButton'); + await retry.tryForTime(15 * 1000, async () => { + await testSubjects.clickWhenNotDisabled( + 'mlAnalyticsCreateJobWizardDetailsStep active > mlAnalyticsCreateJobWizardContinueButton' + ); await this.assertValidationStepActive(); }); }, @@ -456,8 +472,10 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async continueToCreateStep() { - await retry.tryForTime(5000, async () => { - await testSubjects.clickWhenNotDisabled('mlAnalyticsCreateJobWizardContinueButton'); + await retry.tryForTime(15 * 1000, async () => { + await testSubjects.clickWhenNotDisabled( + 'mlAnalyticsCreateJobWizardValidationStepWrapper active > mlAnalyticsCreateJobWizardContinueButton' + ); await this.assertCreateStepActive(); }); }, diff --git a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts index 5abc842ddc9c9..81e868c5a38cb 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts @@ -17,9 +17,10 @@ export function FleetIntegrations({ getService, getPageObjects }: FtrProviderCon return { async navigateToIntegrationDetails(pkgkey: string) { - await pageObjects.common.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - hash: pagePathGetters.integration_details_overview({ pkgkey })[1], - }); + await pageObjects.common.navigateToUrlWithBrowserHistory( + INTEGRATIONS_PLUGIN_ID, + pagePathGetters.integration_details_overview({ pkgkey })[1] + ); }, async integrationDetailCustomTabExistsOrFail() { diff --git a/yarn.lock b/yarn.lock index 4d7370d1f527b..04261da41ea05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1617,11 +1617,6 @@ history "^4.9.0" qs "^6.7.0" -"@elastic/ui-ace@0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@elastic/ui-ace/-/ui-ace-0.2.3.tgz#5281aed47a79b7216c55542b0675e435692f20cd" - integrity sha512-Nti5s2dplBPhSKRwJxG9JXTMOev4jVOWcnTJD1TOkJr1MUBYKVZcNcJtIVMSvahWGmP0B/UfO9q9lyRqdivkvQ== - "@emotion/babel-plugin-jsx-pragmatic@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.1.5.tgz#27debfe9c27c4d83574d509787ae553bf8a34d7e" @@ -6951,11 +6946,6 @@ angular-sortable-view@^0.0.17: resolved "https://registry.yarnpkg.com/angular-sortable-view/-/angular-sortable-view-0.0.17.tgz#99e2679951a86b6ee6ff27b099022943c683fb4f" integrity sha512-2WkhM0Lt/wyMyrX/+7ve9ejSegBd7A4eRBNHEIJz8XMBIOjt+3oM1WpcAm+qNThkmNmmQaDeaYv0TQZw/WDMBw== -angular-ui-ace@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/angular-ui-ace/-/angular-ui-ace-0.2.3.tgz#3cb903428100621a367fc7f641440e97a42a26d0" - integrity sha1-PLkDQoEAYho2f8f2QUQOl6QqJtA= - angular@>=1.0.6, angular@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.0.tgz#b1ec179887869215cab6dfd0df2e42caa65b1b51"